[Debugger] Avoid spurious timeout on empty collection serialization#8741
Conversation
BenchmarksBenchmark execution time: 2026-06-03 14:16:32 Comparing candidate commit 723eb8c in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 72 metrics, 0 unstable metrics, 59 known flaky benchmarks, 67 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8741) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (75ms) : 71, 79
master - mean (76ms) : 72, 80
section Bailout
This PR (8741) - mean (78ms) : 75, 80
master - mean (79ms) : 75, 82
section CallTarget+Inlining+NGEN
This PR (8741) - mean (1,110ms) : 1057, 1164
master - mean (1,109ms) : 1054, 1164
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (115ms) : 110, 120
master - mean (115ms) : 108, 121
section Bailout
This PR (8741) - mean (116ms) : 113, 119
master - mean (115ms) : 112, 119
section CallTarget+Inlining+NGEN
This PR (8741) - mean (800ms) : 772, 827
master - mean (797ms) : 773, 821
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (102ms) : 98, 105
master - mean (101ms) : 98, 104
section Bailout
This PR (8741) - mean (106ms) : 102, 110
master - mean (107ms) : 102, 111
section CallTarget+Inlining+NGEN
This PR (8741) - mean (955ms) : 916, 993
master - mean (950ms) : 916, 983
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (101ms) : 97, 104
master - mean (100ms) : 98, 103
section Bailout
This PR (8741) - mean (104ms) : 99, 109
master - mean (101ms) : 99, 103
section CallTarget+Inlining+NGEN
This PR (8741) - mean (829ms) : 791, 866
master - mean (825ms) : 788, 862
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (199ms) : 194, 204
master - mean (201ms) : 197, 205
section Bailout
This PR (8741) - mean (203ms) : 198, 208
master - mean (204ms) : 199, 209
section CallTarget+Inlining+NGEN
This PR (8741) - mean (1,203ms) : 1165, 1240
master - mean (1,214ms) : 1173, 1256
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (288ms) : 279, 296
master - mean (292ms) : 285, 299
section Bailout
This PR (8741) - mean (289ms) : 283, 295
master - mean (291ms) : 285, 297
section CallTarget+Inlining+NGEN
This PR (8741) - mean (967ms) : 946, 988
master - mean (968ms) : 947, 989
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (280ms) : 273, 287
master - mean (280ms) : 274, 287
section Bailout
This PR (8741) - mean (280ms) : 272, 288
master - mean (279ms) : 274, 284
section CallTarget+Inlining+NGEN
This PR (8741) - mean (1,159ms) : 1123, 1196
master - mean (1,163ms) : 1120, 1207
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8741) - mean (279ms) : 271, 287
master - mean (280ms) : 274, 286
section Bailout
This PR (8741) - mean (278ms) : 270, 286
master - mean (280ms) : 273, 286
section CallTarget+Inlining+NGEN
This PR (8741) - mean (1,040ms) : 1002, 1078
master - mean (1,042ms) : 1000, 1083
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 723eb8ca59
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // empty (Count == 0) or fully-serialized collection must never be reported as a | ||
| // timeout, even if the serialization budget happened to elapse in the meantime on a | ||
| // slow/loaded machine. | ||
| if (cts.IsCancellationRequested && itemIndex < collection.Count) |
There was a problem hiding this comment.
Preserve timeout when mutable collections shrink
For supported mutable/concurrent collections such as ConcurrentQueue<T>/ConcurrentBag<T>, collection.Count can change while the snapshot enumerator still has more items. If the serialization budget expires at the top of the loop after some items were written and the collection has since shrunk so that the current Count <= itemIndex, this guard suppresses notCapturedReason: timeout even though remaining enumerator items were skipped, making the captured snapshot look complete.
Useful? React with 👍 / 👎.
| // empty (Count == 0) or fully-serialized collection must never be reported as a | ||
| // timeout, even if the serialization budget happened to elapse in the meantime on a | ||
| // slow/loaded machine. | ||
| if (cts.IsCancellationRequested && itemIndex < collection.Count) |
There was a problem hiding this comment.
itemIndex doesn't necessarily reflect the actual index, but we can merge it anyway if it resolves the flakiness. In any case, I'd like to make some additional improvements in that area so we can improve it later.
Summary of changes
Only report
notCapturedReason: timeoutfor a collection when there are still elements left to serialize (itemIndex < collection.Count).Reason for change
Flaky test
Datadog.Trace.Tests.Debugger.DebuggerSnapshotCreatorTests.ObjectStructure_EmptyArray. Logs hereThe serializer starts a wall-clock cancellation budget (
DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE, default 200ms) at the start of serialization. For an empty collection there's nothing to serialize, but if that budget happened to elapse before the post-loopcts.IsCancellationRequestedcheck (slow/loaded CI machine, GC pause), the empty array was wrongly stamped withtimeout:Implementation details
SerializeEnumerablenow guards the timeout branch withitemIndex < collection.Count, so an empty (Count == 0) or fully-serialized collection can never be reported as a timeout regardless of the budget. A collection cancelled mid-serialization still reportstimeout.Test coverage
Covered by the existing
ObjectStructure_EmptyArray/ObjectStructure_EmptyListverified tests.Other details
Replaces the earlier mitigation that bumped the test timeout to 1000ms — a shared mutable static that could still be clobbered (by
DebuggerManager.SetConfig) or exceeded, so it only reduced the probability rather than fixing the spurious behavior.