Skip to content

chore(remoteconfig): refresh client identity on runtime refresh - #19818

Closed
litianningdatadog wants to merge 1 commit into
tianning.li/2-flask-web-request-starting-eventfrom
tianning.li/3-1-remoteconfig-identity-client
Closed

litianningdatadog wants to merge 1 commit into
tianning.li/2-flask-web-request-starting-eventfrom
tianning.li/3-1-remoteconfig-identity-client

Conversation

@litianningdatadog

@litianningdatadog litianningdatadog commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Stacked PRs:

Description

Split from #19780.

Remote Config clients include process/runtime identity in their generated client id, and the native Remote Config client keeps that id in its own state. When runtime identity is refreshed for a restored MicroVM instance, rebuild the Remote Config client identity and drop the stale native client so future requests use the refreshed identity.

Testing

  • scripts/lint format_check ddtrace/internal/remoteconfig/client.py tests/internal/remoteconfig/test_remoteconfig_native.py
  • git diff --check

Stack

Depends on #19816.

@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
@cit-pr-commenter-54b7da

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

Copy link
Copy Markdown

Codeowners resolved as

Resolved from the full PR diff against tianning.li/2-flask-web-request-starting-event using the target branch CODEOWNERS file.
CODEOWNERS team requests not listed below are not required by the current file set.

ddtrace/internal/remoteconfig/client.py                                 @DataDog/remote-config @DataDog/apm-core-python
tests/internal/remoteconfig/test_remoteconfig_native.py                 @DataDog/remote-config @DataDog/apm-core-python

@cit-pr-commenter-54b7da

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

Copy link
Copy Markdown

Circular import analysis

⚠️ Existing circular imports

There are 3 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
ddtrace.llmobs -> ddtrace.llmobs._evaluators -> ddtrace.llmobs._evaluators.format -> ddtrace.llmobs._experiment -> ddtrace.llmobs
ddtrace.appsec._asm_request_context -> ddtrace.appsec._iast._iast_request_context_base -> ddtrace.appsec._iast._iast_env -> ddtrace.appsec._iast.reporter -> ddtrace.appsec._exploit_prevention.stack_traces -> ddtrace.appsec._asm_request_context

@cit-pr-commenter-54b7da

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

Copy link
Copy Markdown

Dependency direction analysis

⚠️ Existing dependency direction violations

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

Show existing violations (showing 5 of 250 highest severity)
ddtrace.internal.tracemethods -×-> ddtrace.trace  (internal-core -> product:tracing, score=135)
ddtrace.llmobs._evaluators.runner -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)
ddtrace.llmobs._integrations.vertexai -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)
ddtrace.llmobs._integrations.bedrock -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=133)
ddtrace.internal.opentelemetry.context -×-> ddtrace.trace  (product:opentelemetry -> product:tracing, score=133)

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

@litianningdatadog litianningdatadog changed the title fix(remoteconfig): refresh client identity on runtime refresh chore(remoteconfig): refresh client identity on runtime refresh Aug 23, 2026
@datadog-prod-us1-5

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

Copy link
Copy Markdown
Contributor

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: e9a58bc | 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-08-23 22:23:08

Comparing candidate commit 3e763c0 in PR branch tianning.li/3-1-remoteconfig-identity-client with baseline commit d59e112 in branch tianning.li/2-flask-web-request-starting-event.

📊 Benchmarking dashboard

Found 0 performance improvements and 10 performance regressions! Performance is the same for 612 metrics, 10 unstable metrics.

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:httppropagationinject-ids_only

  • 🟥 execution_time [+2.009µs; +2.210µs] or [+10.501%; +11.550%]

scenario:iastaspects-add_aspect

  • 🟥 execution_time [+14.503µs; +18.243µs] or [+14.308%; +17.998%]

scenario:iastaspects-join_aspect

  • 🟥 execution_time [+48.155µs; +52.033µs] or [+22.761%; +24.594%]

scenario:iastaspects-ljust_noaspect

  • 🟥 execution_time [+61.771µs; +66.015µs] or [+21.705%; +23.197%]

scenario:iastaspects-title_noaspect

  • 🟥 execution_time [+29.311µs; +33.463µs] or [+14.999%; +17.123%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+144.547µs; +149.975µs] or [+35.431%; +36.762%]

scenario:iastaspectssplit-rsplit_aspect

  • 🟥 execution_time [+16.594µs; +21.953µs] or [+11.579%; +15.318%]

scenario:span-start

  • 🟥 execution_time [+1.458ms; +1.600ms] or [+9.896%; +10.864%]

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

  • 🟥 execution_time [+444.116ns; +496.305ns] or [+16.478%; +18.414%]

scenario:tracer-small

  • 🟥 execution_time [+26.370µs; +28.613µs] or [+7.810%; +8.474%]

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 [-648.212ns; +841.395ns] or [-5.882%; +7.635%]

scenario:coreapiscenario-core_dispatch_1_listener

  • unstable execution_time [-32.301ns; +34.033ns] or [-5.300%; +5.584%]

scenario:coreapiscenario-core_dispatch_50_listeners

  • unstable execution_time [-1651.484ns; +1698.057ns] or [-9.607%; +9.878%]

scenario:coreapiscenario-core_dispatch_exception_listeners

  • unstable execution_time [-1097.152ns; +1380.020ns] or [-8.524%; +10.721%]

scenario:coreapiscenario-core_dispatch_listeners

  • unstable execution_time [-300.988ns; +356.964ns] or [-8.180%; +9.701%]

scenario:coreapiscenario-core_dispatch_no_args_listeners

  • unstable execution_time [-278.579ns; +229.758ns] or [-9.510%; +7.843%]

scenario:coreapiscenario-core_dispatch_with_results_1_listener

  • unstable execution_time [-97.921ns; +48.341ns] or [-8.463%; +4.178%]

scenario:coreapiscenario-core_dispatch_with_results_50_listeners

  • unstable execution_time [-3382.122ns; +4546.634ns] or [-8.478%; +11.398%]

scenario:coreapiscenario-core_dispatch_with_results_listeners

  • unstable execution_time [-705.994ns; +835.274ns] or [-8.839%; +10.457%]

scenario:packagesupdateimporteddependencies-import_many_stdlib_cached

  • unstable execution_time [-61592.105ns; +59899.871ns] or [-9.571%; +9.308%]

@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-1-remoteconfig-identity-client branch from 3e763c0 to ff3e857 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-1-remoteconfig-identity-client branch from ff3e857 to 25a94b2 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-1-remoteconfig-identity-client branch from 25a94b2 to 997466f Compare August 25, 2026 13:22
@litianningdatadog
litianningdatadog requested a lite review from Copilot August 25, 2026 14:08

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 internal Remote Config client to respond to runtime-identity refreshes (e.g., AWS Lambda MicroVM restore) by regenerating its client ID and forcing a rebuild of the native Remote Config client so subsequent polls use fresh identity.

Changes:

  • Subscribe RemoteConfigClient to runtime ID change notifications and renew its client ID on refresh.
  • Drop the cached native Remote Config client on identity refresh so ensure_native() rebuilds it with refreshed runtime identity.
  • Add native Remote Config tests validating client-id renewal, native client dropping/rebuild, and wiring to runtime.refresh_identity().

Reviewed changes

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

File Description
ddtrace/internal/remoteconfig/client.py Subscribes to runtime ID changes and drops/rebuilds native Remote Config client identity on refresh.
tests/internal/remoteconfig/test_remoteconfig_native.py Adds coverage for client-id renewal and native client rebuild behavior on identity refresh.

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

Comment thread ddtrace/internal/remoteconfig/client.py Outdated
Comment thread ddtrace/internal/remoteconfig/client.py
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-1-remoteconfig-identity-client branch from 997466f to 6e15d59 Compare August 25, 2026 14:59
@litianningdatadog
litianningdatadog force-pushed the tianning.li/3-1-remoteconfig-identity-client branch from 6e15d59 to e9a58bc Compare August 25, 2026 15:10
@litianningdatadog
litianningdatadog marked this pull request as ready for review August 25, 2026 15:11
@litianningdatadog
litianningdatadog requested review from a team as code owners August 25, 2026 15:11
@litianningdatadog
litianningdatadog requested review from emmettbutler and removed request for a team August 25, 2026 15:11

@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: e9a58bc42f

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

log.debug("failed to create remote config reader after fork", exc_info=True)
else:
self._reader = None
self.renew_id()

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 the AppSec RC client-id cache

When runtime.refresh_identity() runs while AppSec Remote Configuration is enabled, this renews the client ID, but enable_appsec_rc() cached the previous value in asm_config._rc_client_id only once (ddtrace/appsec/_remoteconfiguration.py:83). finalize_asm_env() continues tagging every AppSec span with that stale value (ddtrace/appsec/_asm_request_context.py:403-404), while subsequent RC requests use the new ID, breaking correlation between those spans and the RC client after an identity refresh. Propagate the renewed ID to this cache or make the span tag read the current client ID.

Useful? React with 👍 / 👎.

Comment on lines +158 to +159
self.renew_id()
self._native = None

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 Preserve broadcasts to already-forked RC consumers

When refresh_identity() runs in an origin process after it has forked workers, dropping this native client also drops the writer for the shared-memory segments inherited by those workers. The replacement client starts with local storage, and existing children cannot inherit its new handles—the native protocol requires enable_shared_memory() before the fork (src/native/remote_config.rs:9-12)—so they remain attached to the frozen old mappings and silently stop receiving future Remote Configuration updates, including security configuration. The refresh path needs to preserve or migrate the existing broadcast publisher rather than simply replacing it in an already-forked origin.

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

Useful? React with 👍 / 👎.

Comment on lines +158 to +159
self.renew_id()
self._native = None

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 Serialize native creation with identity refresh

When identity refresh overlaps the first ensure_native() call while _native is still None—for example, one thread enables RC while a restore hook refreshes identity on another—the constructor can already have read the old self.id but not yet assigned its result. These lines then renew the ID and clear _native, after which the racing constructor assigns the old-identity native client back into _native; it remains cached, so every later poll continues using the stale client identity despite the completed refresh. Native construction/assignment and identity invalidation need a shared synchronization or generation check.

Useful? React with 👍 / 👎.

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

Datadog Autotest: FAIL

A runtime identity refresh changes the Remote Config client ID. AppSec keeps the old ID and adds it to later spans, so Remote Config data cannot match those spans.

Open Bits AI session

🤖 Datadog Autotest · Commit e9a58bc · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

log.debug("failed to create remote config reader after fork", exc_info=True)
else:
self._reader = None
self.renew_id()

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.

P2 Refresh the AppSec client ID after identity renewal

Remote Config data cannot match AppSec spans from a restored runtime.

Assertion details
  • Input: Enable AppSec Remote Config. Then refresh the runtime identity and finalize a later AppSec span.
  • Expected: AppSec spans must use the new Remote Config client ID after a runtime identity refresh.
  • Actual: The refresh handler renews RemoteConfigClient.id. AppSec still keeps the old value in asm_config._rc_client_id and adds it to later spans.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

Comment on lines +104 to +113
client_ref = weakref.ref(self)

# Runtime keeps callbacks in a module-level set, so registering a bound
# method would keep this client alive after callers drop their reference.
def _on_identity_refresh(new_runtime_id: str) -> None:
client = client_ref()
if client is not None:
client._on_identity_refresh(new_runtime_id)

on_runtime_id_change(_on_identity_refresh)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we should probably just add a remove_on_runtime_id_change(func) method, then this can all go away and we get:

def __init__(self):
    on_runtime_id_change(self._on_identity_refresh)

def __del__(self):
    remove_on_runtime_id_change(self._on_identity_refresh)

Comment on lines +144 to +145
# as immutable constructor arguments (get_client_id() is documented "stable for
# the process lifetime"). The next ensure_native() call rebuilds it bound to the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given this definition of "stable for the process lifetime", the new changes to refresh the runtime id at runtime seem to go against this expectation.

Should MicroVM be using a different identifier for this instead of runtime-id?

Do we need to change all expectations in shared components in libdatadog and all repos to no longer assume runtime-id is process stable? which is mostly the changes we are making here

@emmettbutler
emmettbutler marked this pull request as draft August 26, 2026 17:43

@emmettbutler emmettbutler left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Marked as draft because the base branch is not main

@litianningdatadog

Copy link
Copy Markdown
Contributor Author

close it as it is out of the scope

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 changelog/no-changelog A changelog entry is not required for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants