Skip to content

feat: deterministic sampling with adaptive sample size#1849

Merged
kodiakhq[bot] merged 4 commits intomainfrom
event-deltas/sampling
Mar 5, 2026
Merged

feat: deterministic sampling with adaptive sample size#1849
kodiakhq[bot] merged 4 commits intomainfrom
event-deltas/sampling

Conversation

@alex-fedotyev
Copy link
Contributor

@alex-fedotyev alex-fedotyev commented Mar 5, 2026

Summary

Closes #1827

Replaces non-deterministic ORDER BY rand() with deterministic cityHash64(SpanId) sampling and introduces sampling configuration constants.

What this PR does

  • Deterministic sampling: ORDER BY cityHash64(SpanId) instead of rand() — same data always produces the same sample, so results are stable across re-renders
  • Named constants: SAMPLE_SIZE, STABLE_SAMPLE_EXPR replace hardcoded 1000 and 'rand()' in query configs
  • Adaptive sizing foundation: computeEffectiveSampleSize() function with MIN_SAMPLE_SIZE/MAX_SAMPLE_SIZE/SAMPLE_RATIO constants, exported and tested (6 unit tests)

What this PR does NOT do (follow-up)

  • Count query for adaptive sizing: Wiring computeEffectiveSampleSize into the actual queries requires adding a lightweight count() query. This is deferred to keep this PR focused on the deterministic sampling change.
  • Dynamic column detection: STABLE_SAMPLE_EXPR uses SpanId which is trace-specific. Event Deltas currently only renders on the traces search page where SpanId is always present. If the feature expands to logs/metrics, this should be parameterized per source (documented in code comment).

Test plan

  • Same data + same hover always highlights the same heatmap cells (deterministic)
  • Run npx jest src/components/__tests__/deltaChartSampling.test.ts — 6 tests pass

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Mar 5, 2026 4:24pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

🦋 Changeset detected

Latest commit: 980ab65

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 58s —— View job


Code Review

  • ⚠️ Dead code / incomplete wiringSTABLE_SAMPLE_EXPR, computeEffectiveSampleSize, and all sampling constants in deltaChartUtils.ts are only referenced in the test file. They're never imported into DBDeltaChart.tsx or any query builder, so the deterministic sampling and adaptive sizing described in the PR description aren't actually active yet. → Wire these into the actual query (e.g. the ORDER BY / LIMIT in the delta chart's ClickHouse query), or hold back until they're connected.

  • 🔒 Hardcoded SpanId columnSTABLE_SAMPLE_EXPR = 'cityHash64(SpanId)' will silently produce wrong results (or a ClickHouse error) on log/metric sources that don't have a SpanId column. → Use the source's spanIdExpression dynamically, or pick a universally-present column (e.g. cityHash64(timestamp, RowID)) as the stable hash key.

  • ⚠️ Unrelated changes in the same PR — The onAddFilter prop added to DBDeltaChart.tsx (lines 47–55, 221–229, 421, 458) has nothing to do with sampling. This mixes two separate features, making review and rollback harder. → Split into its own PR, or clearly call this out in the description.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

E2E Test Results

All tests passed • 86 passed • 3 skipped • 941s

Status Count
✅ Passed 86
❌ Failed 0
⚠️ Flaky 2
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

- Replace ORDER BY rand() with cityHash64(SpanId) for deterministic sampling
- Replace hardcoded LIMIT 1000 with SAMPLE_SIZE constant
- Add computeEffectiveSampleSize() for adaptive sizing (clamp 500-5000)
- Add STABLE_SAMPLE_EXPR, SAMPLE_RATIO constants
- 6 unit tests for computeEffectiveSampleSize

Closes #1827

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alex-fedotyev alex-fedotyev force-pushed the event-deltas/sampling branch from 841885d to ff88d90 Compare March 5, 2026 01:03
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 39s —— View job


Code Review

  • computeEffectiveSampleSize is imported but never called (DBDeltaChart.tsx:25) → All three queries still use the hardcoded SAMPLE_SIZE (1000) constant; the adaptive sizing is dead code. The count query described in the PR summary also doesn't exist. Either wire up the count query + call computeEffectiveSampleSize with its result, or remove the import and the function until it's ready.

  • 🔒 STABLE_SAMPLE_EXPR = 'cityHash64(SpanId)' hardcodes a trace-only column (deltaChartUtils.ts:306) → This will cause a ClickHouse error or silent wrong results on log/metric sources that don't have SpanId. Use a universally-present column (e.g., cityHash64(toString(timestamp))) or derive the expression dynamically from the source's schema.

…scope

- Remove dead import of computeEffectiveSampleSize (adaptive sizing
  via count query will be added in a follow-up)
- Add comment explaining STABLE_SAMPLE_EXPR uses SpanId because Event
  Deltas is currently trace-specific; should be parameterized if the
  feature expands to logs/metrics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 37s —— View job


Code Review

  • computeEffectiveSampleSize is never called — the three queries all use the static SAMPLE_SIZE = 1000. The PR description promises "adaptive sample sizing" but it's not wired up. Either call it with a count query result or remove it until ready. Fix this →

  • ⚠️ STABLE_SAMPLE_EXPR = 'cityHash64(SpanId)' is trace-only (deltaChartUtils.ts:309) — will cause a ClickHouse error on log/metric sources without a SpanId column. Use a universally-present column like cityHash64(toString(timestamp)), or accept the current scope limitation with a clear code comment (one already exists, which is good, but the risk is real if the component is ever reused).

@alex-fedotyev
Copy link
Contributor Author

@claude - repeat code review against the updated PR description and give aligned feedback

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 1m 17s —— View job


Code Review (aligned with PR description)

  • Read changed files
  • Post aligned review

The latest commit (70023920) cleaned up the previously flagged unused import of computeEffectiveSampleSize. Here's a review aligned with what the PR actually claims to do and explicitly defers:

What this PR does well:

  • All 3 queries in DBDeltaChart.tsx correctly swap ORDER BY rand()ORDER BY cityHash64(SpanId) DESC and 1000SAMPLE_SIZE — the core deterministic sampling is live and working.
  • Named constants replace magic numbers throughout, making future adjustments trivial.
  • The SpanId scope limitation is clearly documented inline (deltaChartUtils.ts:298–304) — no silent failure risk for the current use case (traces-only page).
  • computeEffectiveSampleSize is exported, tested (6 passing tests), and not imported in production code — this correctly matches the PR's stated intent to defer the count query wiring.

One genuine concern not previously raised:

  • ⚠️ ORDER BY … DESC LIMIT is a biased top-N, not a statistical sample (DBDeltaChart.tsx:141, 196, 205) — ORDER BY cityHash64(SpanId) DESC LIMIT 1000 always returns the same 1000 rows: those with the largest hash values. For the stated goal of stable hover highlights across re-renders, this is exactly right. But it's worth knowing this isn't a statistically representative sample — if the data is large enough that 1000 rows is a meaningful subset, the selected rows are always a fixed population (highest-hash spans), not a random draw. This could matter if, e.g., certain span types cluster in hash space. For the current heatmap use case it's acceptable, but worth a brief comment like // always selects the same top-N rows by hash — biased but stable across renders.

Previous review concerns vs. current state:

Concern Status
computeEffectiveSampleSize imported but unused ✅ Fixed in latest commit
Adaptive sizing not wired ✅ Explicitly deferred — PR description is clear
STABLE_SAMPLE_EXPR / SpanId scope ✅ Documented with code comment; scoped risk is acceptable
Unrelated onAddFilter changes Not present in current diff — may have been on an older version

Overall the implementation matches the PR description accurately. The only call-out is the "biased sample" nuance, which is a minor documentation gap rather than a bug.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 22s —— View job


Code Review

✅ No critical issues found.

The implementation matches the PR description accurately:

  • ORDER BY cityHash64(SpanId) DESC LIMIT 1000 is correctly wired into all 3 queries
  • computeEffectiveSampleSize is exported/tested but intentionally not called yet (deferred per PR description)
  • SpanId scope limitation is documented inline

One minor note: ORDER BY cityHash64(SpanId) DESC LIMIT 1000 is a deterministic top-N (always the same 1000 rows), not a statistical sample. This is correct for stable hover highlights but worth knowing if representative sampling is ever needed.

@alex-fedotyev alex-fedotyev marked this pull request as ready for review March 5, 2026 15:33
@alex-fedotyev alex-fedotyev requested review from a team and pulpdrew and removed request for a team March 5, 2026 15:35
export const MIN_SAMPLE_SIZE = 500;
export const MAX_SAMPLE_SIZE = 5000;
export const SAMPLE_RATIO = 0.01;
export const STABLE_SAMPLE_EXPR = 'cityHash64(SpanId)';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should refer to the source's spanIdExpression - it may not always be SpanId. If the user has a custom schema, I suspect this would break.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 980ab65. Replaced the hardcoded STABLE_SAMPLE_EXPR constant with getStableSampleExpression(spanIdExpression?) that accepts the source's spanIdExpression (passed from DBSearchHeatmapChart which already has the source). Falls back to rand() when not available.

Address review feedback from @pulpdrew:
- Replace hardcoded STABLE_SAMPLE_EXPR constant with getStableSampleExpression()
  that accepts the source's spanIdExpression, falling back to rand()
- Pass spanIdExpression from source through DBSearchHeatmapChart -> DBDeltaChart
- Apply JSDoc per-constant style for better editor hover docs
- Add 4 tests for getStableSampleExpression (10 total)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude finished @alex-fedotyev's task in 38s —— View job


Code Review

No critical issues. The implementation matches the PR description accurately.

  • getStableSampleExpression(spanIdExpression?) correctly uses the source's spanIdExpression (passed from DBSearchHeatmapChart.tsx:122) and falls back to rand() — the previously flagged hardcoded SpanId concern is resolved.
  • All 3 queries in DBDeltaChart.tsx use stableSampleExpr and SAMPLE_SIZE — deterministic sampling is wired and active.
  • computeEffectiveSampleSize is exported + tested but intentionally not called — matches the PR's stated deferral.
  • JSDoc comments are now on each constant as suggested by the reviewer.

One minor note (not blocking): ORDER BY cityHash64(spanId) DESC LIMIT 1000 is a deterministic top-N, not a statistically representative sample — it always selects the same highest-hash rows. This is the correct behavior for stable hover highlights, but worth a brief inline comment if representative sampling is ever a future goal.

@kodiakhq kodiakhq bot merged commit 68ef3d6 into main Mar 5, 2026
20 of 21 checks passed
@kodiakhq kodiakhq bot deleted the event-deltas/sampling branch March 5, 2026 16:52
alex-fedotyev pushed a commit that referenced this pull request Mar 5, 2026
Resolve conflicts: keep both onAddFilter (filter-actions) and
spanIdExpression (sampling, merged via #1849) props.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event Deltas: Deterministic sampling with adaptive sample size

2 participants