Skip to content

Implement stable session identifier headers for telemetry#295

Draft
khanayan123 wants to merge 12 commits intomainfrom
ayan.khan/stable-session-id-headers
Draft

Implement stable session identifier headers for telemetry#295
khanayan123 wants to merge 12 commits intomainfrom
ayan.khan/stable-session-id-headers

Conversation

@khanayan123
Copy link

@khanayan123 khanayan123 commented Mar 20, 2026

Summary

Implements the Stable Service Instance Identifier RFC for the C++ SDK.

  • Adds DD-Session-ID (= runtime_id) and conditional DD-Root-Session-ID headers to all telemetry requests (app-started, heartbeats, app-closing, etc.)
  • DD-Root-Session-ID is only sent when it differs from DD-Session-ID (i.e., in child processes)
  • Root session ID is read from _DD_ROOT_CPP_SESSION_ID via the config registry (environment::lookup) or defaults to the current runtime_id
  • environment::set(_DD_ROOT_CPP_SESSION_ID, ...) is called at tracer init so exec'd children inherit it automatically (no-op if already set)
  • Fork propagation is automatic — memory survives fork, so the root session ID persists in child processes

Files changed

  • include/datadog/environment.h — Added _DD_ROOT_CPP_SESSION_ID to DD_LIST_ENVIRONMENT_VARIABLES registry; added environment::set() declaration
  • src/datadog/environment.cpp — Added environment::set() implementation (platform-aware, no-overwrite)
  • include/datadog/tracer_signature.h — Added root_session_id member to TracerSignature
  • src/datadog/tracer.cpp — Computes root session ID via environment::lookup; registers it via environment::set for exec propagation
  • src/datadog/telemetry/telemetry_impl.cpp — Adds session headers to app_started() and send_payload()
  • supported-configurations.json — Added _DD_ROOT_CPP_SESSION_ID entry
  • Tests — Session header validation: root process (no DD-Root-Session-ID), child process (DD-Root-Session-ID present), heartbeat includes headers

Companion system-tests PR: DataDog/system-tests#6510

Test plan

  • All existing assertions pass (107 test cases)
  • New test: root process sends DD-Session-ID only
  • New test: child process sends both DD-Session-ID and DD-Root-Session-ID
  • New test: heartbeat includes session headers

🤖 Generated with Claude Code

Add DD-Session-ID and DD-Root-Session-ID headers to all telemetry
requests per the Stable Service Instance Identifier RFC.

- DD-Session-ID (= runtime_id) sent on every telemetry request
- DD-Root-Session-ID sent only when root != current runtime_id
- Root session ID inherited from _DD_ROOT_CPP_SESSION_ID env var
  (for exec-based child processes) or defaults to first runtime_id
- setenv propagates root session ID to exec'd children automatically
- Fork propagation is automatic (memory survives fork)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pr-commenter
Copy link

pr-commenter bot commented Mar 20, 2026

Benchmarks

Benchmark execution time: 2026-03-24 19:26:09

Comparing candidate commit a3b7684 in PR branch ayan.khan/stable-session-id-headers with baseline commit 14280af in branch main.

Found 0 performance improvements and 1 performance regressions! Performance is the same for 0 metrics, 0 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:BM_TraceTinyCCSource

  • 🟥 execution_time [+3.152ms; +3.361ms] or [+4.160%; +4.435%]

khanayan123 and others added 3 commits March 23, 2026 13:55
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use environment::lookup() instead of std::getenv() directly, and
register DD_ROOT_CPP_SESSION_ID in DD_LIST_ENVIRONMENT_VARIABLES
per project convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@datadog-prod-us1-6

This comment has been minimized.

_DD_ROOT_CPP_SESSION_ID is an internal propagation variable not in the
public configuration registry. Route it through a new lookup_internal()
helper in environment.cpp (the one file allowed to call std::getenv)
rather than registering it in DD_LIST_ENVIRONMENT_VARIABLES, which would
cause supported-configurations.json to fail remote registry validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setenv() is POSIX-only; Windows requires _putenv_s(). Add set_internal()
to environment.cpp (the platform-appropriate layer) that uses the right
call per platform, and route the tracer's root session ID propagation
through it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace lookup_internal/set_internal (linter-added wrappers) with
direct std::getenv and setenv/_putenv_s calls. The _DD_ROOT_CPP_SESSION_ID
env var is an internal propagation mechanism and does not belong in the
supported config registry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
khanayan123 and others added 5 commits March 24, 2026 10:25
Replace direct std::getenv/setenv calls with environment::lookup()
and environment::set() via the DD_LIST_ENVIRONMENT_VARIABLES registry.
Also add the var to supported-configurations.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Read _DD_ROOT_CPP_SESSION_ID in finalize_config() and store it in
FinalizedTracerConfig, consistent with how all other env vars are
handled. Removes the ad-hoc get_root_session_id() helper in tracer.cpp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces exclusive env var propagation with a shared memory carrier:
- Linux:   memfd_create("dd-session-ids") without MFD_CLOEXEC; child
           discovers via /proc/self/fd/ scan
- macOS:   shm_open("/dd-session-ids-<pid>"); child opens by getppid()
- Windows: CreateFileMapping("Local\dd-session-ids-<pid>"); child
           opens by Toolhelp32-resolved parent PID

Tracer creates the carrier on startup and keeps it alive as a member.
finalize_config() prefers SHM on read, falls back to the env var for
interop with SDKs that still use _DD_ROOT_CPP_SESSION_ID.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SHM is now the sole propagation mechanism. The env var was only ever
written and read by C++ itself, so there was no cross-SDK interop value
in keeping it. Removes: the registry entry, environment::set(), the
write in Tracer constructor, the fallback read in finalize_config, and
the supported-configurations.json entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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