Implement stable session identifier headers for telemetry#295
Implement stable session identifier headers for telemetry#295khanayan123 merged 52 commits intomainfrom
Conversation
BenchmarksBenchmark execution time: 2026-04-10 17:51:36 Comparing candidate commit e61e812 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 1 metrics, 0 unstable metrics.
|
|
🎯 Code Coverage (details) 🔗 Commit SHA: e61e812 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback! |
a3b7684 to
0e65a27
Compare
Adds DD-Session-ID and DD-Root-Session-ID HTTP headers to all telemetry requests per the Stable Service Instance Identifier RFC. - DD-Session-ID is always set to the tracer's runtime_id - DD-Root-Session-ID is only set when it differs from the session ID (i.e. when running as a child process) - Root session ID is propagated to exec'd children via _DD_ROOT_CPP_SESSION_ID env var, read in finalize_config() - _DD_ROOT_CPP_SESSION_ID registered in the environment variable registry and supported-configurations.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85d241a to
e40dd5d
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
src/datadog/tracer.cpp
Outdated
| signature_{runtime_id_, config.defaults.service, | ||
| config.defaults.environment}, | ||
| signature_{runtime_id_, | ||
| config.root_session_id.value_or(runtime_id_.string()), |
There was a problem hiding this comment.
This is going to sound really weird, but I have a suggestion for updating this line of code. I think Method 1 Meyers Singleton from this Google AI response would be the best way to initialize the root session ID singleton. If we change the singleton implementation to align with that, then what we will be doing is:
- We are guaranteed to have a valid
runtime_id_from the previous line - Right after we've set the
runtime_id_field, we try to initialize the Singleton root session ID with theruntime_id_value, but the returned value will always be the singleton from the firstruntime_id_that was set - Once we get the singleton instance back, then we get the string value so we can assign it to the
root_session_idfield
| config.root_session_id.value_or(runtime_id_.string()), | |
| Singleton::getInstance(runtime_id_.string()).getValue(), |
Replace separate set()/get() with a single get_or_init(runtime_id) that initializes on first call and returns the stored value on subsequent calls. Remove root_session_id from TracerConfig/FinalizedTracerConfig — the singleton is now the sole source of truth, read directly in Tracer construction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify get_or_init returns the first runtime ID on subsequent calls with different values (first-writer-wins semantics). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 3-param constructor now calls root_session_id::get_or_init internally, so every TracerSignature construction via the short form goes through the singleton. Tracer no longer needs to call get_or_init directly. Session header tests use the 4-param constructor to control values directly without depending on singleton state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The singleton may already be set from a prior test case. Compare the child's result against what get_or_init actually returned, not the runtime ID passed in this test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use root_session_id::get_or_init to acquire the root session ID and use it throughout the session header test cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep the 3-param TracerSignature constructor as a simple inline delegating constructor. The get_or_init call stays in tracer.cpp where the Tracer is constructed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
xlamorlette-datadog
left a comment
There was a problem hiding this comment.
Please review the singleton implementation.
src/datadog/root_session_id.cpp
Outdated
| return instance(); | ||
| } | ||
|
|
||
| } // namespace root_session_id |
There was a problem hiding this comment.
nit, pedantic, personal preference: I think such comments are noise
| : RuntimeID::generate()), | ||
| signature_{runtime_id_, config.defaults.service, | ||
| config.defaults.environment}, | ||
| signature_{runtime_id_, |
There was a problem hiding this comment.
nit: I agree one should generalize the usage of braces initialization calls, but here I think we should favor consistency with existing code
- Simplify singleton to static const Meyer's pattern, remove .cpp file - Use C++17 compound namespaces in root_session_id.h - Rename sig -> signature in telemetry_impl.cpp - Remove singleton usage from tests, use 3-param constructor for match - Add blank lines between constructors in tracer_signature.h - Fix extra spaces in test Telemetry init alignment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
zacharycmontoya
left a comment
There was a problem hiding this comment.
LGTM. Thanks for incorporating all our feedback, this is a much more simplified implementation now! Please also get approval @xlamorlette-datadog before merging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clang-format requires the extra spaces for argument alignment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Using the longer variable name avoids clang-format's column alignment padding while matching the naming convention in the rest of the file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrations (nginx, httpd, kong) need to set root_session_id in the master process before workers fork. The Tracer passes it to get_or_init, falling back to runtime_id if not provided. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| // Root session ID for stable telemetry correlation across forked workers. | ||
| // Integrations (nginx, httpd, kong) should set this in the master process | ||
| // before workers fork so all Tracers share the same root. | ||
| Optional<std::string> root_session_id; |
There was a problem hiding this comment.
nit: we should have the same order for the fields in TracerConfig and FinalizedTracerConfig.
| : RuntimeID::generate()), | ||
| signature_{runtime_id_, config.defaults.service, | ||
| config.defaults.environment}, | ||
| signature_{runtime_id_, |
…redundant if checks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
That's clang-format's alignment, CI's check-format step enforces it |
|
@khanayan123 You forgot to solve the following comments (that you agreed): |
Hmm, must've got lost in one of the commits. Opening up a follow up |
Summary
Implements the Stable Service Instance Identifier RFC for the C++ SDK.
DD-Session-ID(=runtime_id) and conditionalDD-Root-Session-IDheaders to all telemetry requests (app-started, heartbeats, app-closing, etc.)DD-Root-Session-IDis only sent when it differs fromDD-Session-ID(i.e., in child processes)static constlocal), set once by the firstTracerto initialize. Immutable after init, so fork-safe.Integration testing note
System-tests for
cpp_nginxandcpp_httpdcannot validate session headers until the downstream modules (nginx-datadog, httpd-datadog) release a version that includes these dd-trace-cpp changes. The system-tests manifest entries remainmissing_featureuntil then. Unit tests in this PR validate the implementation directly.Companion system-tests PR: DataDog/system-tests#6510