Skip to content

Explain slow and stalled remote config applies - #7625

Open
christophe-papazian wants to merge 4 commits into
mainfrom
christophe-papazian/rc-apply-diagnostics
Open

Explain slow and stalled remote config applies#7625
christophe-papazian wants to merge 4 commits into
mainfrom
christophe-papazian/rc-apply-diagnostics

Conversation

@christophe-papazian

@christophe-papazian christophe-papazian commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

APPSEC-69943

An RC apply that is not acknowledged in time currently surfaces only as assert <UNKNOWN: 0> == <ACKNOWLEDGED: 2>, which says nothing about why. Chasing one of these took a long time for want of information that was already being collected.

  • report every apply that takes over 5s or times out, with elapsed time, expected vs last-seen targets_version, and which configs were unacknowledged
  • log the warnings the library sent through telemetry: libraries report their own slow remote config callbacks there, and system-tests already captures it
  • capture the weblog thread stacks at 10s and 20s into a stalled apply, so a thread parked on one call can be told from one that is moving. Read from outside the process (py-spy for python, added to their images; jcmd for java). Other libraries stay silent
  • fix state being shadowed by two loop variables, which made the timeout message report fields off a config-state dict instead of the client state

Nothing new is sent to the library and nothing is logged for a healthy apply, which stays on a debug line. Every diagnostic path is exception-safe.

Verified by replaying the telemetry of a real failure, which now surfaces the cause directly:

[WARN] RC callback operation exceeded threshold (product:asm_features,callback_type:payload,elapsed_time:30.542)

Threshold note: 5s is calibrated on python (20 000 measured applies, max 1.80s). If another library routinely acknowledges more slowly, SLOW_APPLY_THRESHOLD is a single constant to raise.

🤖 Generated with Claude Code

christophe-papazian and others added 3 commits September 1, 2026 13:53
An RC apply that is not acknowledged in time surfaces only as
`assert <UNKNOWN: 0> == <ACKNOWLEDGED: 2>`, which says nothing about why.
The information needed is usually already there: libraries report their own
slow remote config callbacks through telemetry, and system-tests collects it.

- measure every apply, and report it when it takes longer than 5s or times out
- on those, log the warnings the library sent through telemetry
- give the timeout message the elapsed time, expected and last-seen targets
  version, and which configs were still unacknowledged
- fix `state` being shadowed by two loop variables, which made the timeout
  message report fields off a config-state dict instead of the client state

Nothing new is sent to the library and nothing is logged for a healthy apply,
which stays on a debug line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Telemetry says an RC callback was slow, but not where it was stuck. Capture
the weblog's thread stacks while a stalled apply is still in progress, at 10s
and 20s in, so a thread parked on one call can be told from one that is moving.

Stacks are read from outside the process, which cannot perturb or crash the
process being measured. py-spy covers the python weblogs and is added to their
images; java uses jcmd, already present. Other libraries are not covered and
stay silent.

An apply that completes before the first deadline captures nothing, so healthy
runs are unaffected. Every failure path is swallowed: no weblog in the scenario,
no command for the library, or the exec failing all return quietly rather than
raising in the watcher thread.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

utils/_remote_config.py                                                 @DataDog/system-tests-core
utils/build/docker/python/anthropic-py.Dockerfile                       @DataDog/system-tests-reviewers
utils/build/docker/python/django-poc.Dockerfile                         @DataDog/system-tests-reviewers
utils/build/docker/python/django-py3.13.Dockerfile                      @DataDog/system-tests-reviewers
utils/build/docker/python/fastapi.Dockerfile                            @DataDog/system-tests-reviewers
utils/build/docker/python/flask-poc.Dockerfile                          @DataDog/system-tests-reviewers
utils/build/docker/python/google_genai-py.Dockerfile                    @DataDog/system-tests-reviewers
utils/build/docker/python/openai-py.Dockerfile                          @DataDog/system-tests-reviewers
utils/build/docker/python/python3.12.Dockerfile                         @DataDog/system-tests-reviewers
utils/build/docker/python/tornado.Dockerfile                            @DataDog/system-tests-reviewers
utils/build/docker/python/uds-flask.Dockerfile                          @DataDog/system-tests-reviewers
utils/build/docker/python/uwsgi-poc.Dockerfile                          @DataDog/system-tests-reviewers
utils/interfaces/_core.py                                               @DataDog/system-tests-core

@datadog-prod-us1-4

datadog-prod-us1-4 Bot commented Sep 1, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

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

CI on the previous commit was green, but its logs were not: 140 spurious slow
warnings, 5 "timed out" errors on passing runs, and 5 stack dumps that were
really `jcmd: executable file not found` logged as though they were dumps.

- raise SLOW_APPLY_THRESHOLD to 12s. 5s was calibrated on python (under 2s),
  but java and golang normally take around 8s and reach 10s
- drop the java/jcmd command: jcmd is not in the java weblog image. Stack
  dumps are python-only now, and unsupported libraries create no watcher
- check exit_code, so a failed command is reported as a failure instead of
  being logged under a "thread stacks" heading
- bound the dump with `timeout` in the container, and pass --nonblocking so
  reading stacks does not pause the process being measured
- measure elapsed inside the wait, so watcher teardown is not counted as
  part of the apply, and pass the caller's start time so deadlines measure
  the wait rather than when the thread was scheduled; skip missed deadlines
- report a non-acknowledged apply as a warning, not an error: it does not
  always fail a test. Log it before the sleep so an outer timeout cannot
  lose it
- drop the global dedupe set: every line carries a unique elapsed_time, so it
  never deduplicated and grew without bound. Deduplicate within one report,
  and cap both the number of warnings and the size of a dump
- guard thread start, and annotate the context manager's return type
- snapshot _data_list under the lock in get_data(): the watchdog thread
  appends to and re-sorts it, which could make a walk skip or repeat entries

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@christophe-papazian
christophe-papazian marked this pull request as ready for review September 2, 2026 08:12
@christophe-papazian
christophe-papazian requested review from a team as code owners September 2, 2026 08:12
@christophe-papazian
christophe-papazian requested review from bouwkast and removed request for a team September 2, 2026 08:12
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 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-09-02T08:22:10.656218Z 7fdac44 Draft marked ready
ℹ️ 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: 7fdac44ea4

ℹ️ 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 utils/_remote_config.py
_STACK_DUMP_COMMANDS: dict[str, str] = {
# `timeout` bounds the command so a hung dump cannot extend the apply, and
# --nonblocking keeps py-spy from pausing the process we are measuring
"python": f"timeout {STACK_DUMP_TIMEOUT} py-spy dump --pid 1 --subprocesses --nonblocking",

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 Remove the unsupported py-spy dump flag

When a Python RC apply exceeds 10 seconds, this command exits immediately instead of capturing any stacks because py-spy 0.4.1's dump --help does not list --subprocesses among the dump options; that flag is supported by other py-spy modes such as record. Consequently every scheduled dump reaches the nonzero-exit diagnostic path, defeating the primary stalled-apply investigation feature. Invoke dump without this flag and explicitly select the relevant Python PIDs if subprocess coverage is required.

Useful? React with 👍 / 👎.

Comment thread utils/_remote_config.py
_STACK_DUMP_COMMANDS: dict[str, str] = {
# `timeout` bounds the command so a hung dump cannot extend the apply, and
# --nonblocking keeps py-spy from pausing the process we are measuring
"python": f"timeout {STACK_DUMP_TIMEOUT} py-spy dump --pid 1 --subprocesses --nonblocking",

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 Grant SYS_PTRACE before invoking py-spy

On standard Linux Docker hosts with the usual Yama ptrace restrictions, this stack-dump invocation still fails with permission denied even after the unsupported flag is removed: py-spy's Docker instructions require the target container to have SYS_PTRACE, while the checked WeblogContainer path supplies only security_opt=["seccomp=unconfined"] and adds SYS_PTRACE only for the PHP/C++ core-dump path in utils/_context/containers.py. Grant the capability to Python weblog containers (or use an external privileged profiler), otherwise stalled Python applies produce no stacks in CI.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant