Skip to content

fix(tracer): isolate MicroVM identity refresh - #19820

Open
litianningdatadog wants to merge 1 commit into
tianning.li/3-wsgi-asgi-integration-based-refreshfrom
tianning.li/3-3-trace-writer-identity-refresh
Open

litianningdatadog wants to merge 1 commit into
tianning.li/3-wsgi-asgi-integration-based-refreshfrom
tianning.li/3-3-trace-writer-identity-refresh

Conversation

@litianningdatadog

@litianningdatadog litianningdatadog commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Stacked PRs:

Description

An explicit AWS Lambda MicroVM identity refresh changes the runtime ID, but tracer aggregation state, writer buffers, native exporter state, and active request context may still belong to the prior logical runtime.

This change subscribes the global tracer to explicit MicroVM identity-refresh notifications. During refresh it:

  • Recreates the span-aggregator writer so native exporters use the new runtime ID.
  • Drops buffered writer data, resets aggregation state, and stops the replaced CI Visibility writer without flushing stale telemetry.
  • Serializes the transition with span completion and tags spans with an identity generation, preventing invalidated traces from reaching the replacement writer or side-effecting trace processors.
  • Stops stale spans before trace.span_finish listeners and registered span processors run.
  • Detaches every active snapshot-inherited context, including a remote context. The /run hook fires before distributed-tracing header extraction, so any active remote context at refresh time belongs to the snapshot; extraction then activates the new request's real incoming context.
  • Preserves pending post-fork writer recreation on failure so a later refresh can retry it.

The identity-transition lock and callbacks are enabled only in MicroVM mode. Normal non-MicroVM and post-fork behavior remains unchanged.

The stale-buffer reset previously proposed in #20089 is included because it is part of the tracer and writer identity-refresh behavior.

Reference

Testing

  • Added MicroVM coverage that verifies an invalidated span cannot reach a finish listener or span processor.
  • Added MicroVM coverage that verifies a snapshot-inherited remote context is detached and that subsequently extracted distributed-tracing headers parent the new request correctly.
  • scripts/run-tests -s --venv 4fcf978 -- tests/tracer/runtime/test_runtime_id.py — 46 passed.
  • scripts/lint fmt, scripts/lint typing, and scripts/lint checks passed.

Risks

Pre-refresh trace and telemetry data is intentionally discarded during an explicit MicroVM identity refresh so it cannot be exported under a stale runtime ID. The transition-specific locking is limited to MicroVM identity-refresh state.

@litianningdatadog litianningdatadog added changelog/no-changelog A changelog entry is not required for this PR. aws-microvm Work related to AWS MicroVM onboarding labels Aug 23, 2026
@litianningdatadog litianningdatadog changed the title fix(tracer): rebuild native writer exporter on identity refresh chore(tracer): rebuild native writer exporter on identity refresh Aug 23, 2026
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Tests

✅ All CI checks and tests passed.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: a23e56e | Docs | View more details | Give us feedback!

@pr-commenter

pr-commenter Bot commented Aug 23, 2026 •

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-10-01 18:50:36

Comparing candidate commit a23e56e in PR branch tianning.li/3-3-trace-writer-identity-refresh with baseline commit 42a0b1f in branch tianning.li/3-wsgi-asgi-integration-based-refresh.

📊 Benchmarking dashboard

Found 0 performance improvements and 8 performance regressions! Performance is the same for 577 metrics, 10 unstable metrics, 7 known flaky benchmarks, 17 flaky benchmarks without significant changes.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:httppropagationextract-empty_headers

  • 🟥 execution_time [+90.901ns; +106.281ns] or [+12.291%; +14.370%]

scenario:iastaspects-add_aspect

  • 🟥 execution_time [+9.055µs; +10.849µs] or [+10.788%; +12.926%]

scenario:iastaspects-format_map_noaspect

  • 🟥 execution_time [+102.478µs; +109.623µs] or [+29.024%; +31.048%]

scenario:iastaspects-title_aspect

  • 🟥 execution_time [+107.200µs; +113.881µs] or [+41.286%; +43.859%]

scenario:iastaspectsremodule-re_expand_aspect

  • 🟥 execution_time [+78.252µs; +86.289µs] or [+12.492%; +13.775%]

scenario:msgpackencoderscenario-simple_one_span

  • 🟥 execution_time [+476.297ns; +539.387ns] or [+11.571%; +13.104%]

scenario:otelspan-start

  • 🟥 execution_time [+2.431ms; +3.263ms] or [+9.730%; +13.061%]

scenario:recursivecomputation-shallow

  • 🟥 execution_time [+68.689µs; +72.810µs] or [+9.706%; +10.288%]

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:coreapiscenario-context_with_data_listeners

  • unstable execution_time [-762.197ns; +696.563ns] or [-7.286%; +6.659%]

scenario:coreapiscenario-core_dispatch_1_listener

  • unstable execution_time [-28.569ns; +49.928ns] or [-4.364%; +7.627%]

scenario:coreapiscenario-core_dispatch_50_listeners

  • unstable execution_time [-2024.055ns; +1815.348ns] or [-10.166%; +9.117%]

scenario:coreapiscenario-core_dispatch_exception_listeners

  • unstable execution_time [-1920.459ns; +1714.271ns] or [-10.118%; +9.032%]

scenario:coreapiscenario-core_dispatch_listeners

  • unstable execution_time [-400.746ns; +359.603ns] or [-9.420%; +8.453%]

scenario:coreapiscenario-core_dispatch_no_args_listeners

  • unstable execution_time [-241.394ns; +216.758ns] or [-9.028%; +8.107%]

scenario:coreapiscenario-core_dispatch_with_results_1_listener

  • unstable execution_time [-107.513ns; +78.252ns] or [-7.921%; +5.766%]

scenario:coreapiscenario-core_dispatch_with_results_50_listeners

  • unstable execution_time [-4902.208ns; +4427.367ns] or [-10.134%; +9.153%]

scenario:coreapiscenario-core_dispatch_with_results_listeners

  • unstable execution_time [-996.325ns; +898.698ns] or [-9.733%; +8.779%]

scenario:packagesupdateimporteddependencies-import_many_stdlib_cached

  • unstable execution_time [-54564.466ns; +54506.235ns] or [-9.607%; +9.597%]

Known flaky benchmarks

These benchmarks are marked as flaky and will not trigger a failure. Modify FLAKY_BENCHMARKS_REGEX to control which benchmarks are marked as flaky.

scenario:httppropagationinject-ids_only

  • 🟥 execution_time [+2.987µs; +3.097µs] or [+21.179%; +21.952%]

scenario:iastaspects-rstrip_aspect

  • 🟥 execution_time [+119.545µs; +124.073µs] or [+36.876%; +38.273%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+145.121µs; +150.109µs] or [+38.489%; +39.812%]

scenario:iastaspectssplit-rsplit_aspect

  • 🟥 execution_time [+32.564µs; +37.215µs] or [+21.329%; +24.376%]

scenario:span-start

  • 🟥 execution_time [+2.089ms; +2.527ms] or [+16.568%; +20.039%]

scenario:telemetryaddmetric-1-count-metric-1-times

  • 🟥 execution_time [+266.891ns; +306.014ns] or [+14.124%; +16.195%]

scenario:tracer-small

  • 🟥 execution_time [+59.054µs; +60.616µs] or [+23.983%; +24.618%]

Known flaky benchmarks without significant changes:

  • scenario:errortrackingflasksqli-baseline
  • scenario:flasksimple-iast-get
  • scenario:iastaspects-casefold_aspect
  • scenario:iastaspects-casefold_noaspect
  • scenario:iastaspects-index_aspect
  • scenario:iastaspects-ljust_noaspect
  • scenario:iastaspects-lower_aspect
  • scenario:iastaspects-replace_aspect
  • scenario:iastaspects-swapcase_aspect
  • scenario:iastaspects-title_noaspect
  • scenario:iastaspects-translate_aspect
  • scenario:iastaspects-translate_noaspect
  • scenario:iastaspects-upper_noaspect
  • scenario:packagespackageforrootmodulemapping-cache_off
  • scenario:packagespackageforrootmodulemapping-cache_on
  • scenario:sethttpmeta-all-enabled
  • scenario:telemetryaddmetric-record-100-metrics

@litianningdatadog
litianningdatadog force-pushed the tianning.li/2-flask-web-request-starting-event branch from d59e112 to 16a5332 Compare August 24, 2026 02:23
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-3-trace-writer-identity-refresh branch from 3c0ea31 to eabf5ec Compare August 24, 2026 02:24
@litianningdatadog
litianningdatadog force-pushed the tianning.li/2-flask-web-request-starting-event branch from 16a5332 to 8dd7e8e Compare August 24, 2026 02:31
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-3-trace-writer-identity-refresh branch from eabf5ec to 5853b4a Compare August 24, 2026 02:31
@litianningdatadog
litianningdatadog force-pushed the tianning.li/2-flask-web-request-starting-event branch 5 times, most recently from cde3045 to a0e3c42 Compare August 24, 2026 23:56
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-3-trace-writer-identity-refresh branch from 5853b4a to 7afb3c1 Compare August 25, 2026 13:23
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Codeowners resolved as

Resolved from the full PR diff against tianning.li/3-wsgi-asgi-integration-based-refresh using the target branch CODEOWNERS file.
CODEOWNERS team requests not listed below are not required by the current file set.

ddtrace/__init__.py                                                     @DataDog/python-guild
ddtrace/_trace/context.py                                               @DataDog/apm-sdk-capabilities-python
ddtrace/_trace/processor/__init__.py                                    @DataDog/apm-sdk-capabilities-python
ddtrace/_trace/span.py                                                  @DataDog/apm-sdk-capabilities-python @DataDog/apm-core-python
ddtrace/_trace/tracer.py                                                @DataDog/apm-sdk-capabilities-python
ddtrace/internal/ci_visibility/writer.py                                @DataDog/ci-app-libraries
ddtrace/internal/writer/writer.py                                       @DataDog/apm-core-python
ddtrace/trace/__init__.py                                               @DataDog/apm-sdk-capabilities-python
releasenotes/notes/fix-aws-lambda-microvm-stale-traces-a26d7bf374c1b2c2.yaml  @DataDog/apm-python
tests/tracer/runtime/test_runtime_id.py                                 @DataDog/apm-sdk-capabilities-python
tests/tracer/test_writer.py                                             @DataDog/apm-sdk-capabilities-python

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Circular import analysis

⚠️ Existing circular imports

There are 1 circular imports that already exist on the base branch and have not been changed by this PR.

ddtrace.errortracking._handled_exceptions.bytecode_injector -> ddtrace.errortracking._handled_exceptions.callbacks -> ddtrace.errortracking._handled_exceptions.collector -> ddtrace.errortracking._handled_exceptions.bytecode_reporting -> ddtrace.errortracking._handled_exceptions.bytecode_injector

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Dependency direction analysis

⚠️ Existing dependency direction violations

There are 201 dependency direction violations that already exist on the base branch and have not been changed by this PR.

Show existing violations (showing 5 of 201 highest severity)
ddtrace.internal.tracemethods -×-> ddtrace.trace  (internal-core -> product:tracing, score=132)
ddtrace.llmobs._integrations.base -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=130)
ddtrace.llmobs._integrations.langgraph -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=130)
ddtrace.llmobs._integrations.openai_agents -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=130)
ddtrace.llmobs._integrations.claude_agent_sdk -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=130)

To see all violations, download the layers-base.json and layers-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/layers.py compare layers-base.json layers-pr.json

Copilot AI 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.

Pull request overview

This PR updates the native trace writer so that when runtime identity is refreshed (e.g., AWS Lambda MicroVM /run refresh), the native exporter is rebuilt to pick up the new runtime id captured at exporter construction time.

Changes:

  • Wire NativeWriter to runtime-id change notifications and rebuild the exporter on identity refresh.
  • Add tests to verify the exporter is rebuilt (without recreating the writer/buffer) and that the wiring works end-to-end via runtime.refresh_identity().

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ddtrace/internal/writer/writer.py Subscribes to runtime-id change events and rebuilds the native exporter on identity refresh.
tests/tracer/test_writer.py Adds unit + subprocess tests ensuring identity refresh rebuilds the exporter without replacing the writer’s client/buffer state.
Suppressed comments (2)

ddtrace/internal/writer/writer.py:945

  • _on_identity_refresh can raise if _create_exporter() fails (e.g., native builder/telemetry initialization error). Because runtime.refresh_identity() iterates callbacks without exception handling, a failure here will propagate out of refresh_identity() and can also prevent other runtime-id listeners from running. It’s safer to only swap the exporter after successfully creating the replacement, and to swallow/log failures from the rebuild path.
        old_exporter = self._exporter
        self._exporter = self._create_exporter()
        try:
            old_exporter.shutdown(3_000_000_000)
        except Exception:

ddtrace/internal/writer/writer.py:944

  • Exporter rebuild/swap is not synchronized with payload sending. NativeWriter.flush_queue() can run on the periodic thread and can also be called from other threads, while _on_identity_refresh swaps self._exporter and shuts down the old exporter. Without a lock, this introduces a race where a concurrent _exporter.send() can observe a half-transition or have its exporter shutdown mid-send.

Consider adding a dedicated lock (similar to HTTPWriter._conn_lck) and holding it in _send_payload(), _on_identity_refresh(), set_test_session_token(), and any other place that swaps/shuts down the exporter.

        old_exporter = self._exporter
        self._exporter = self._create_exporter()
        try:
            old_exporter.shutdown(3_000_000_000)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ddtrace/internal/writer/writer.py Outdated
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-wsgi-asgi-integration-based-refresh branch from a637bb2 to 0d53833 Compare September 4, 2026 17:36
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-wsgi-asgi-integration-based-refresh branch 2 times, most recently from 6e5799f to 75def6a Compare September 4, 2026 20:44
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-3-trace-writer-identity-refresh branch 2 times, most recently from f94e6ae to 92a469d Compare September 7, 2026 20:23
@litianningdatadog litianningdatadog removed the changelog/no-changelog A changelog entry is not required for this PR. label Sep 7, 2026
@litianningdatadog litianningdatadog changed the title chore(tracer): rebuild native writer exporter on identity refresh fix(tracer): refresh writer and drop stale buffers on identity refresh Sep 7, 2026
@litianningdatadog
litianningdatadog requested review from a team and removed request for a team September 7, 2026 20:24
@litianningdatadog litianningdatadog added the changelog/no-changelog A changelog entry is not required for this PR. label Sep 7, 2026
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-3-trace-writer-identity-refresh branch from 92a469d to 5a48d93 Compare September 7, 2026 20:31
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T18:21:31.605371Z a23e56e New commits
🔒 Security Review ✅ Completed 2026-10-01T18:23:58.668538Z a23e56e New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f7d597465

ℹ️ 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".

Comment thread ddtrace/internal/writer/writer.py Outdated
Comment thread ddtrace/_trace/processor/__init__.py Outdated
Comment thread ddtrace/_trace/tracer.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a8c91d0c5

ℹ️ 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".

Comment thread ddtrace/_trace/tracer.py
Comment thread ddtrace/_trace/tracer.py
Comment thread ddtrace/_trace/processor/__init__.py Outdated
Comment thread ddtrace/_trace/processor/__init__.py Outdated
Comment thread ddtrace/internal/writer/writer.py
Comment thread ddtrace/_trace/processor/__init__.py Outdated

@datadog-datadog-prod-us1-2 datadog-datadog-prod-us1-2 Bot 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.

Bits Code Review: FAIL

Two of the most critical issues are: (1) on_span_finish in processor/__init__.py mutates trace aggregation state before checking the finishing span's generation, enabling stale spans to corrupt or prematurely export current-generation traces; and (2) start_span in tracer.py assigns the current generation to spans whose parent is a local Context (not a Span), allowing pre-refresh stale traces to pass generation checks and reach the replacement writer.

Open Bits AI session

🤖 Bits Code Review · Commit 0ea9a2f · @DataDog review to ask questions

Comment thread ddtrace/_trace/processor/__init__.py
Comment thread ddtrace/_trace/tracer.py
Comment thread ddtrace/internal/writer/writer.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b29531ab07

ℹ️ 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".

Comment thread ddtrace/_trace/processor/__init__.py
Comment thread ddtrace/_trace/tracer.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb487b59fa

ℹ️ 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".

Comment thread ddtrace/internal/writer/writer.py Outdated
Comment thread ddtrace/_trace/context.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b585e1b387

ℹ️ 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".

Comment thread ddtrace/_trace/tracer.py Outdated
Comment thread ddtrace/_trace/processor/__init__.py
Comment thread ddtrace/_trace/tracer.py
Comment thread ddtrace/_trace/tracer.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94d7001195

ℹ️ 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".

Comment on lines +505 to 508
if identity_generation is not None and not self._identity_generation_is_current(identity_generation):
return
try:
result = tp.process_trace(spans) or []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Serialize identity refresh through each trace processor

If identity refresh begins after this generation check but while tp.process_trace() is executing, a side-effecting processor can still publish stale data; for example, LLMObsProcessor routes events during the call and CIVisibilitySpanForwarder writes immediately. Fresh evidence beyond the earlier comment is the remaining unlocked gap between the check and the entire processor invocation, so checking only before each processor does not prevent the processor currently running from exporting after the generation changes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I narrowed this to the trace-writer invariant for this PR: stale APM traces must not be exported after refresh begins. The LLMObs/CI examples use product-specific processors outside the RFC MVP scope for #19820, so I did not serialize arbitrary product/custom processors here.

Comment thread ddtrace/trace/__init__.py
if in_aws_lambda_microvm():
from ddtrace.internal._runtime_id import on_runtime_identity_refresh

on_runtime_identity_refresh(tracer._refresh_runtime_identity)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh every live tracer on identity rotation

In a MicroVM application that creates a public Tracer() instance instead of using the singleton, this registers only the module-level tracer for refresh. The /run hook therefore rotates the process-wide runtime ID while the custom tracer retains its inherited trace buffer, generation, and identity-bound native exporter; subsequent spans can carry the new runtime tag through an exporter configured with the snapshot identity. Use lifecycle-safe registration that covers every live tracer rather than only this singleton.

AGENTS.md reference: AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping this scoped to the module-level tracer. Multiple manually-created public Tracer() instances are already unsupported (Tracer.__init__ logs an error after the singleton exists), and the MicroVM MVP wiring in this stack attaches the refresh callback to the global tracer.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 701fe64544

ℹ️ 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".

Comment thread ddtrace/_trace/tracer.py Outdated
Comment thread ddtrace/internal/writer/writer.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a74cd2e33

ℹ️ 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".

# Only explicit MicroVM refreshes use discard-and-recreate semantics.
identity_refresh = drop_buffered_traces and not flush_writer and self._identity_refresh_enabled
if identity_refresh:
with self._lock:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reset the aggregator lock after a fork

When a MicroVM forks while another application thread owns SpanAggregator._lock, the child inherits this raw ddtrace.internal.threads.RLock in the locked state. The first /run refresh then blocks forever on this newly added with self._lock, because _child_after_fork() resets the trace buffer but never replaces the lock. Fresh evidence beyond the earlier generation-table issue is that the aggregator's separate lock remains non-fork-safe; use forksafe.RLock() or recreate it in the child hook.

AGENTS.md reference: AGENTS.md:L135-L135

Useful? React with 👍 / 👎.

Comment on lines +1319 to 1322
if self._writer_lock is not None and not self._accepting_writes:
return
try:
self._send_payload(encoded, n_traces, client)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make the send gate atomic with identity refresh

When a periodic flush passes this _accepting_writes check just before refresh sets the flag to false, it still calls _send_payload() with the stale payload. drop_buffered_traces() changes the flag outside _writer_lock and then waits for that lock, so the active flush can send—and potentially retry—after runtime-ID rotation before releasing the lock. Fresh evidence beyond the earlier active-flush thread is this remaining check-to-send gap; the transition must invalidate the payload atomically with the send rather than relying on this pre-send check.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6b50c3b38

ℹ️ 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".

Comment on lines +657 to +659
identity_refresh = drop_buffered_traces and not flush_writer and self._identity_refresh_enabled
if identity_refresh:
with self._lock:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Serialize every writer recreation with identity refresh

When an AppSec remote-config callback calls Tracer.configure() concurrently with the first MicroVM /run, its normal reset path at lines 667-674 does not acquire this lock. It can begin recreating the old writer before the runtime ID rotates, then assign that old-identity writer after this branch has installed the refreshed writer; subsequent current-generation traces are consequently exported with stale runtime metadata. The transition lock must cover normal writer swaps as well as this identity-refresh branch.

Useful? React with 👍 / 👎.

Comment on lines +1292 to +1294
with writer_lock:
for client in self._clients:
getattr(client.encoder, "flush")()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reset encoder locks before discarding post-fork buffers

In a MicroVM that forks while another application thread is inside encoder.put() or encoder.flush(), the writer's new fork-safe lock is reset in the child but the msgpack encoder's own raw _thread.RLock is not. The first /run refresh therefore reaches this flush() and blocks forever on a lock owned by a vanished parent thread. Recreate/reset the encoder lock in the child, or discard the inherited encoder without acquiring it.

AGENTS.md reference: AGENTS.md:L135-L135

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94a8535645

ℹ️ 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".

Comment thread ddtrace/_trace/tracer.py
Comment on lines +679 to +680
else:
_set_runtime_identity_generation(child_of, cast(int, identity_generation))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject untracked contexts retained across refresh

When a Context was created before the snapshot but was never active or used to start a span, it has no side-table entry; after refresh, passing that retained context as child_of reaches this fallback and labels it with the current generation, so the span is accepted and exported as a child of the stale pre-refresh trace. Fresh evidence beyond the earlier retained-context comment is this explicit promotion of every untracked context to the current generation; creation/extraction needs to record the generation so pre-refresh and newly extracted contexts can be distinguished.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aws-microvm Work related to AWS MicroVM onboarding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants