Skip to content

fix: prevent duplicate trace_id after CallTraceHashTable expansion - #659

Merged
jbachorik merged 5 commits into
mainfrom
sphinx/fix-jira-PROF-15396
Jul 16, 2026
Merged

fix: prevent duplicate trace_id after CallTraceHashTable expansion#659
jbachorik merged 5 commits into
mainfrom
sphinx/fix-jira-PROF-15396

Conversation

@jbachorik

@jbachorik jbachorik commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?:
CallTraceHashTable::put() could assign the same trace_id to two different call traces once the table expanded: capacity doubles on expansion but the new LongHashTable generation's slot numbering restarted from 0, while instance_id (the upper 32 bits of trace_id) stays fixed until the whole table retires. This PR threads a slot_base through each LongHashTable generation so the low 32 bits of trace_id (slot_base + slot) stay unique across all generations of one active tenure, and adds an overflow guard (with a CALLTRACE_STORAGE_EXPANSION_SKIPPED counter) that skips expansion rather than let slot_base + capacity carry into instance_id's bits.

Motivation:
An ASan stress test showed ~4% duplicate trace IDs after expansion, causing downstream JFR constant-pool/symbolication consumers to merge or misattribute samples for the colliding traces.

Additional Notes:
The expansion-overflow guard's boundary check and capacity-doubling logic are extracted into two pure, allocation-free static methods (CallTraceHashTable::wouldExceedSlotIdRange, nextGenerationCapacity) so they can be unit tested directly — reaching the 2^32 boundary via real put() calls would require billions of inserts.

How to test the change?:
New/updated gtest cases in test_callTraceStorage.cpp:

  • ExpansionProducesNoDuplicateTraceIds
  • TraceIdStableAcrossExpansion
  • TwoExpansionsWithinOneTenureNoDuplicateTraceIds
  • CallTraceHashTableOverflowGuardTest.RejectsOnlyValuesThatExceedSlotIdRange
  • CallTraceHashTableOverflowGuardTest.NextGenerationCapacityIsDouble

Run via :ddprof-lib:gtestDebug_test_callTraceStorage; all 18 cases pass.

For Datadog employees:

  • If this PR touches code that signs or publishes builds or packages, or handles
    credentials of any kind, I've requested a security review (run the dd:platform-security-review
    skill, or file a request via the PSEC review form).
    bewaire also runs automatically on every PR.
  • This PR doesn't touch any of that.
  • JIRA: PROF-15396

Unsure? Have a question? Request a review!

…ROF-15396)

Thread slot_base through LongHashTable generations so trace_id low bits stay
unique across expansions instead of restarting from 0; guard against the
slot range overflowing 2^32 with a counter for observability.
@jbachorik jbachorik added the AI label Jul 14, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #29488003810 | Commit: a4672cc | Duration: 14m 26s (longest job)

All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - - -
8-ibm - - -
8-j9 - -
8-librca - -
8-orcl - - -
11 - - -
11-j9 - -
11-librca - -
17 - -
17-graal - -
17-j9 - -
17-librca - -
21 - -
21-graal - -
21-librca - -
25 - -
25-graal - -
25-librca - -

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-07-16 09:57:33 UTC

@dd-octo-sts

dd-octo-sts Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit a6ab878)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124506625 Commit: a6ab87878db9d09981bf1530e4df4c4f0c267fe0

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 21): runtime -2.9% (2847→2764 ms)
  • 🔴 future-genetic (JDK 25): runtime +3.6% (2028→2101 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10287 ms (21 iters) ✅ 10265 ms (21 iters) ≈ -0.2% (±11.4%) — / —
akka-uct 25 ✅ 8865 ms (24 iters) ✅ 8823 ms (24 iters) ≈ -0.5% (±9.4%) — / —
finagle-chirper 21 ✅ 5948 ms (33 iters) ✅ 6028 ms (33 iters) ≈ +1.3% (±25.7%) ⚠️ W:4 / ⚠️ W:3
finagle-chirper 25 ✅ 5455 ms (36 iters) ✅ 5496 ms (36 iters) ≈ +0.8% (±24.1%) ⚠️ W:4 / ⚠️ W:3
fj-kmeans 21 ✅ 2847 ms (66 iters) ✅ 2764 ms (68 iters) 🟢 -2.9% — / —
fj-kmeans 25 ✅ 2811 ms (66 iters) ✅ 2804 ms (66 iters) ≈ -0.2% (±2.6%) — / —
future-genetic 21 ✅ 2063 ms (90 iters) ✅ 2083 ms (89 iters) ≈ +1% (±2.7%) — / —
future-genetic 25 ✅ 2028 ms (91 iters) ✅ 2101 ms (88 iters) 🔴 +3.6% — / —
naive-bayes 21 ✅ 1273 ms (134 iters) ✅ 1273 ms (135 iters) ≈ 0% (±33%) — / —
naive-bayes 25 ✅ 1013 ms (169 iters) ✅ 1013 ms (170 iters) ≈ 0% (±31.4%) — / —
reactors 21 ✅ 15847 ms (15 iters) ✅ 16518 ms (15 iters) ≈ +4.2% (±7.9%) — / —
reactors 25 ✅ 18435 ms (15 iters) ✅ 18690 ms (15 iters) ≈ +1.4% (±4.3%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / ✅ 1947 / 1968 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 2288 / 2290 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 1 / 3 8620 / 8482 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8154 / 8569 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ ✅ / 3 1268 / 1261 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 2 1302 / 1258 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 3 / 1 2907 / 2901 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / 2 2961 / 2852 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 3 / 5 3511 / 3546 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 1 / 7 3475 / 3486 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 1641 / 1558 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 1 1886 / 1870 ✅ / ✅ ✅ / ✅

@dd-octo-sts

dd-octo-sts Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 9747d1a)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124510382 Commit: 9747d1ac9df614b76e08905a8081ade783ac6d48

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10295 ms (21 iters) ✅ 10393 ms (21 iters) ≈ +1% (±11.7%) — / —
akka-uct 25 ✅ 8929 ms (24 iters) ✅ 8879 ms (24 iters) ≈ -0.6% (±9.9%) — / —
finagle-chirper 21 ✅ 5918 ms (33 iters) ✅ 5995 ms (33 iters) ≈ +1.3% (±25.7%) ⚠️ W:4 / ⚠️ W:3
finagle-chirper 25 ✅ 5466 ms (36 iters) ✅ 5523 ms (36 iters) ≈ +1% (±24.6%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2725 ms (68 iters) ✅ 2722 ms (68 iters) ≈ -0.1% (±2.7%) — / —
fj-kmeans 25 ✅ 2813 ms (66 iters) ✅ 2818 ms (66 iters) ≈ +0.2% (±2.6%) — / —
future-genetic 21 ✅ 2054 ms (90 iters) ✅ 2102 ms (88 iters) ≈ +2.3% (±2.7%) — / —
future-genetic 25 ✅ 2087 ms (89 iters) ✅ 2056 ms (91 iters) ≈ -1.5% (±2.6%) — / —
naive-bayes 21 ✅ 1237 ms (138 iters) ✅ 1245 ms (137 iters) ≈ +0.6% (±33%) — / —
naive-bayes 25 ✅ 1009 ms (169 iters) ✅ 1021 ms (167 iters) ≈ +1.2% (±31.7%) — / —
reactors 21 ✅ 16431 ms (15 iters) ✅ 16392 ms (15 iters) ≈ -0.2% (±7.2%) — / —
reactors 25 ✅ 18177 ms (15 iters) ✅ 18521 ms (15 iters) ≈ +1.9% (±3.8%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 4 / 1 2020 / 1951 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 3 / 1 2243 / 2310 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 4 / 3 8170 / 8615 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 1 8215 / 8832 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 4 1271 / 1271 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 3 / 2 1278 / 1303 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 3 / ✅ 2904 / 2968 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 2924 / 2882 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 2 / 5 3484 / 3496 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 4 / 6 3457 / 3490 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1540 / 1604 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / ✅ 1866 / 1850 ✅ / ✅ ✅ / ✅

…ment

Drive expandTableIfNeeded()'s slot-id overflow guard through a real put()
call path via a test-only seed seam, and note the skip state is bounded by
the next processTraces() rotation.
@jbachorik
jbachorik marked this pull request as ready for review July 15, 2026 07:54
@jbachorik
jbachorik requested a review from a team as a code owner July 15, 2026 07:54
@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 6e66143)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124689199 Commit: 6e661433eda29ac042c22fef7b83fd43e03da06d

⚠️ Significant outliers

  • 🟢 fj-kmeans (JDK 25): runtime -3.3% (2846→2752 ms)
  • 🔴 future-genetic (JDK 21): runtime +3.5% (2074→2146 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10374 ms (21 iters) ✅ 10289 ms (21 iters) ≈ -0.8% (±11.2%) — / —
akka-uct 25 ✅ 8897 ms (24 iters) ✅ 8853 ms (24 iters) ≈ -0.5% (±10.7%) — / —
finagle-chirper 21 ✅ 5946 ms (33 iters) ✅ 5918 ms (33 iters) ≈ -0.5% (±25.7%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5450 ms (36 iters) ✅ 5485 ms (36 iters) ≈ +0.6% (±24.4%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2690 ms (70 iters) ✅ 2752 ms (68 iters) ≈ +2.3% (±2.8%) — / —
fj-kmeans 25 ✅ 2846 ms (66 iters) ✅ 2752 ms (68 iters) 🟢 -3.3% — / —
future-genetic 21 ✅ 2074 ms (89 iters) ✅ 2146 ms (86 iters) 🔴 +3.5% — / —
future-genetic 25 ✅ 2042 ms (91 iters) ✅ 2057 ms (91 iters) ≈ +0.7% (±2.7%) — / —
naive-bayes 21 ✅ 1257 ms (135 iters) ✅ 1266 ms (135 iters) ≈ +0.7% (±32.7%) — / —
naive-bayes 25 ✅ 1011 ms (169 iters) ✅ 1011 ms (169 iters) ≈ 0% (±31.6%) — / —
reactors 21 ✅ 17102 ms (15 iters) ✅ 16692 ms (15 iters) ≈ -2.4% (±7.6%) — / —
reactors 25 ✅ 18787 ms (15 iters) ✅ 18071 ms (15 iters) ≈ -3.8% (±5.6%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 2027 / 1956 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 2307 / 2112 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 2 8895 / 8258 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ ✅ / 1 8318 / 8518 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 4 / 2 1271 / 1291 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1260 / 1274 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 3 / 2 2956 / 2989 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 1 / 1 2815 / 2888 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 5 / 5 3489 / 3492 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 3 / 5 3491 / 3497 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / 1 1670 / 1600 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1899 / 1819 ✅ / ✅ ✅ / ✅

@zhengyu123 zhengyu123 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good.

Nits:
nextGenerationCapacity() and wouldExceedSlotIdRange() can be private.

…ssor

Addresses PR review nit: nextGenerationCapacity()/wouldExceedSlotIdRange()
were public solely for direct test access. Route the test through a new
CallTraceHashTableOverflowGuardTestAccessor friend, matching the existing
CallTraceHashTableTestAccessor convention.
@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 7735121)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/124963941 Commit: 7735121cbfd0f1e258ac59672c26c90d8ab71546

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +5.9% (2663→2821 ms)
  • 🟢 future-genetic (JDK 25): runtime -3.7% (2102→2025 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10192 ms (21 iters) ✅ 10333 ms (21 iters) ≈ +1.4% (±11.4%) — / —
akka-uct 25 ✅ 8866 ms (24 iters) ✅ 8870 ms (24 iters) ≈ +0% (±9.8%) — / —
finagle-chirper 21 ✅ 5965 ms (33 iters) ✅ 6042 ms (33 iters) ≈ +1.3% (±25.1%) ⚠️ W:4 / ⚠️ W:3
finagle-chirper 25 ✅ 5502 ms (36 iters) ✅ 5471 ms (36 iters) ≈ -0.6% (±24.2%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2663 ms (69 iters) ✅ 2821 ms (66 iters) 🔴 +5.9% — / —
fj-kmeans 25 ✅ 2841 ms (66 iters) ✅ 2834 ms (66 iters) ≈ -0.2% (±2.5%) — / —
future-genetic 21 ✅ 2099 ms (88 iters) ✅ 2114 ms (88 iters) ≈ +0.7% (±2.8%) — / —
future-genetic 25 ✅ 2102 ms (88 iters) ✅ 2025 ms (92 iters) 🟢 -3.7% — / —
naive-bayes 21 ✅ 1223 ms (139 iters) ✅ 1248 ms (136 iters) ≈ +2% (±33.4%) — / —
naive-bayes 25 ✅ 1022 ms (168 iters) ✅ 985 ms (174 iters) ≈ -3.6% (±30.8%) — / —
reactors 21 ✅ 16348 ms (15 iters) ✅ 16483 ms (15 iters) ≈ +0.8% (±8.5%) — / —
reactors 25 ✅ 18672 ms (15 iters) ✅ 18638 ms (15 iters) ≈ -0.2% (±4.2%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 1 1973 / 1855 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 1 2102 / 2158 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 4 8665 / 8205 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 8217 / 8297 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 3 1254 / 1268 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1270 / 1297 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ ✅ / 5 2895 / 2962 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 3 / ✅ 2945 / 2833 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 2 / 8 3528 / 3502 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 7 / 6 3475 / 3491 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 1709 / 1726 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 3 / ✅ 1926 / 1933 ✅ / ✅ ✅ / ✅

@jbachorik
jbachorik merged commit dfc29ef into main Jul 16, 2026
105 checks passed
@jbachorik
jbachorik deleted the sphinx/fix-jira-PROF-15396 branch July 16, 2026 10:25
@github-actions github-actions Bot added this to the 1.47.0 milestone Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants