Repository navigation
fix(runtime): reset runtime metric baselines on MicroVM identity refresh - #20957
Conversation
Dependency direction analysis
|
Codeowners resolved asResolved from the full PR diff against |
Circular import analysis
|
|
✅ All CI checks and tests passed. 🎉 All green!🧪 All tests passed 🔗 Commit SHA: 1fa33c7 | Docs | View more details | Give us feedback! |
BenchmarksBenchmark execution time: 2026-10-14 21:00:12 Comparing candidate commit 1fa33c7 in PR branch Found 0 performance improvements and 4 performance regressions! Performance is the same for 359 metrics, 9 unstable metrics, 4 known flaky benchmarks, 4 flaky benchmarks without significant changes.
|
e9cacbf to
e97ac10
Compare
18c7d5e to
bd81cc2
Compare
e97ac10 to
8f5c7f3
Compare
bd81cc2 to
6560882
Compare
bb78835 to
5af6b8e
Compare
6560882 to
3c5a337
Compare
Part 1 stops the old trace writer the normal way. The old writer still flushes traces and client stats buffered before the refresh under the snapshot's runtime ID, /run waits for that final flush, and spans that were open across /run join the new runtime. This change replaces that with a discard: - Writers get drop_buffered_traces(). In MicroVMs it closes the write gate and drops the native exporter without shutting it down. A discarded writer skips its final flush, never starts another send, and its stop() does not wait for a send stuck in retries. - The span aggregator publishes a (generation, runtime ID) identity. A refresh bumps the generation, drops the aggregator's trace buffer and discards the writer under the aggregator lock. Other writer swaps take the same lock. - Spans and retained Context objects from an older generation are rejected before they reach the new writer. The context that was active at /run is detached. - A sync-mode write is buffered under the aggregator lock and sent after the lock is released, so a send stuck in retries cannot block span starts or /run. This replaces part 1's _stop_accepting_writes() with drop_buffered_traces(). It also switches part 1's refresh from _recreate(reset_buffer=False, flush_writer=False) to _recreate(reset_buffer=True, drop_buffered_traces=True). Repeat of the commit on tianning.li/3-3-2part-2-discard-stale-traces, on top of #19821 and #19822 part 1. It drops out once that branch lands and this one is rebased. Combining it with #19821 part 1 needs one extra change: _downgrade() reads the old exporter before calling _create_exporter(), which in MicroVMs publishes its result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…icroVM refresh - _discard_worker() uses the native worker's non-flushing drop() when it has one. It falls back to stop(), which flushes once under the previous runtime ID. The bundled TelemetryWorker does not expose drop() yet, so the fallback is what runs today. - The trace exporter unsubscribes from telemetry worker changes when #19820's refresh drops it (_drop_exporter() and the dropped path of on_shutdown()). Tests return to #19821's drop() versions, and the end-to-end test now checks that the tracer refresh with telemetry does not wait for a stuck send. Repeat of the last commit on tianning.li/3-4-2part-2-discard-stale-telemetry, on top of #19822 part 1. It drops out once that branch lands and this one is rebased. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part 1 makes the runtime-id tag follow the refresh, but the collectors' first interval after /run still covers activity from before the snapshot. That sample is reported under the new runtime ID. - Runtime metric collectors get reset(), which re-seeds their CPU time, context-switch, wall-clock and GC-pause baselines. - In MicroVMs that tag runtime metrics with runtime-id, RuntimeWorker registers for identity refreshes and resets its collectors. It records which runtime ID the baselines belong to. A flush that sees a newer runtime ID resets and skips that interval, which covers a refresh callback that was missed or failed. The reset and the flush share a lock. A fork hook resets again in the child. Part 1's flush() moves under that lock. The wiring test returns to checking the tracer's published identity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3c5a337 to
4f08498
Compare
5af6b8e to
1fa33c7
Compare
Stacked PRs:
/runlistener and identity refresh/runlistener; runtime-metrics runtime-ID tag (part 1 of chore(runtime): refresh runtime metric identity and collector state #19822)Part 1 PRs (#20952, #20953, #20954) can ship first. Part 2 PRs (#20955, #20956, #20957) can ship later.
Description
This PR is part 2 of #19822, on top of #20954. Its tip has exactly the code of #19822.
Review the last commit only. The first two commits repeat #20955's commit and #20956's last commit, so this branch reaches #19822's code without merge commits. They drop out when this branch is rebased after those PRs land.
#20954 makes the runtime-ID tag follow the refresh, but the collectors' first interval after
/runstill covers activity from before the snapshot. That sample is reported under the new runtime ID. The last commit:reset(), which re-seeds their CPU time, context-switch, wall-clock and GC baselines.GCRuntimeMetricCollector.reset()re-seeds the collections baseline before clearing the pause window, so a collection between the two steps can show up as collections without a pause, but never as a pause without collections.RuntimeWorkerregisters for identity refreshes and resets its collectors. It records which runtime ID the baselines belong to (_collectors_runtime_id), updated only after a reset completes.flush()that sees a newer runtime ID resets and skips that interval. That covers a refresh callback that was missed (the worker was created mid-refresh) or failed. A reset that raises there is logged and retried on the next flush, instead of stopping the worker thread._identity_refresh_lock, so a reset never lands in the middle of a send. fix(aws_lambda): refresh the MicroVM identity when a web request starts #20954'sflush()moves under that lock.stop()unregisters both hooks.Testing
tests/runtime/test_runtime_metrics_api.py:test_runtime_metrics_microvm_identity_refresh_resets_collectors,test_runtime_metrics_microvm_refresh_reset_waits_for_in_progress_flush,test_runtime_metrics_microvm_flush_skips_when_id_rotates_during_tag_collectiontest_runtime_metrics_microvm_flush_resets_after_failed_refresh_reset,test_runtime_metrics_microvm_flush_resets_after_refresh_during_construction: a missed or failed reset is caught by the next flush, which sends nothing.test_runtime_metrics_microvm_forked_child_flush_does_not_reset,test_runtime_metrics_microvm_fork_resets_collectors_for_child_id,test_runtime_metrics_microvm_failed_fork_reset_keeps_mismatchtest_runtime_metrics_non_microvm_flush_ignores_runtime_id_change,test_runtime_metrics_non_microvm_identity_refresh_does_not_reset_collectors,test_runtime_metrics_microvm_without_runtime_id_does_not_register_refresh,test_runtime_metrics_microvm_stop_unregisters_refresh_hooktest_runtime_metrics_non_microvm_flush_keeps_platform_tags_cached: also checks that there's no refresh lock outside a MicroVM.tests/tracer/runtime/test_metric_collectors.py: thereset()tests forNativeProcessMetricCollectorandGCRuntimeMetricCollector, including a collection duringGCRuntimeMetricCollector.reset()andreset()on a disabled collector.tests/tracer/runtime/test_runtime_id.py:test_import_ddtrace_in_microvm_wires_run_hook_to_tracergoes back to #19822's check of the tracer's published identity.Local runs (Python 3.13):
runtime(venv16cc321): all tests added here pass (49 passed, 7 failed). The 7 failures are the same older tests that fail on chore(runtime): refresh runtime metric identity and collector state #19822 and on chore(runtime): refresh MicroVM identity #19939's branch (see fix(aws_lambda): refresh the MicroVM identity when a web request starts #20954). Theruntimesuite isskip: trueintests/suitespec.yml.tracer(venv1ef5a52): 226 passed, 10 skipped, 1 xpassed.telemetry(venv7dec5d4): 148 passed.appsec::sca(venv1a7f51a): 127 passed.tracer225 passed after the first, andtracer226 andtelemetry148 passed after the second.scripts/lint styleandscripts/lint typingon the changed files: passed.Risks
Limited to MicroVMs with runtime-ID tagging. Elsewhere nothing is registered and
flush()takes its existing path.Reference
🤖 Generated with Claude Code