fix(rc)!: fix missing/null deserialization and rename tracing_sample_rate#2102
fix(rc)!: fix missing/null deserialization and rename tracing_sample_rate#2102iunanua wants to merge 4 commits into
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 51a7d2a | Docs | Datadog PR Page | Give us feedback! |
📚 Documentation Check Results📦
|
Clippy Allow Annotation ReportComparing clippy allow annotations between branches:
Summary by Rule
Annotation Counts by File
Annotation Stats by Crate
About This ReportThis report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality. |
🔒 Cargo Deny Results📦
|
Artifact Size Benchmark Reportaarch64-alpine-linux-musl
aarch64-unknown-linux-gnu
libdatadog-x64-windows
libdatadog-x86-windows
x86_64-alpine-linux-musl
x86_64-unknown-linux-gnu
|
82ba0f6 to
c0bb7a8
Compare
Wraps every DynamicConfig field in a new `Patch<T>` newtype that encodes JSON Merge Patch (RFC 7396) semantics: - Patch(None) — field absent on the wire (no change intended) - Patch(Some(None)) — field present as JSON null (clear prior override) - Patch(Some(Some(v))) — field present with value (set to v) `Configs` variants now carry `Option<T>` (Some = set, None = clear), and `From<DynamicConfig> for Vec<Configs>` only emits a variant for fields that were actually delivered. This fixes the prior collapse where any APM_TRACING payload missing tracing_sampling_rate looked the same to consumers as `tracing_sampling_rate: null`, so a payload that updated unrelated fields (e.g. tracing_tags) would wipe an active remote sample-rate override. The struct-level `#[serde(default)]` keeps the new shape free of per-field `deserialize_with` boilerplate. Test-feature `Serialize` is a small hand-written impl that skips absent fields to keep the `dummy_dynamic_config` round-trip intact for the sidecar tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
c0bb7a8 to
1e238b0
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f97593726
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| pub(crate) tracing_sampling_rate: Patch<f64>, | ||
| pub(crate) log_injection_enabled: Patch<bool>, | ||
| pub(crate) tracing_tags: Patch<Vec<String>>, | ||
| pub(crate) tracing_enabled: Patch<bool>, | ||
| pub(crate) tracing_sampling_rules: Patch<Vec<TracingSamplingRule>>, | ||
| pub(crate) dynamic_instrumentation_enabled: Patch<bool>, | ||
| pub(crate) exception_replay_enabled: Patch<bool>, | ||
| pub(crate) code_origin_enabled: Patch<bool>, |
There was a problem hiding this comment.
I'm tempted of making them public in order to make it easier to access (and that it isn't necessary to create and iterate over a Vec<Configs> array).
| pub(crate) tracing_sampling_rate: Patch<f64>, | |
| pub(crate) log_injection_enabled: Patch<bool>, | |
| pub(crate) tracing_tags: Patch<Vec<String>>, | |
| pub(crate) tracing_enabled: Patch<bool>, | |
| pub(crate) tracing_sampling_rules: Patch<Vec<TracingSamplingRule>>, | |
| pub(crate) dynamic_instrumentation_enabled: Patch<bool>, | |
| pub(crate) exception_replay_enabled: Patch<bool>, | |
| pub(crate) code_origin_enabled: Patch<bool>, | |
| pub tracing_sampling_rate: Patch<f64>, | |
| pub log_injection_enabled: Patch<bool>, | |
| pub tracing_tags: Patch<Vec<String>>, | |
| pub tracing_enabled: Patch<bool>, | |
| pub tracing_sampling_rules: Patch<Vec<TracingSamplingRule>>, | |
| pub dynamic_instrumentation_enabled: Patch<bool>, | |
| pub exception_replay_enabled: Patch<bool>, | |
| pub code_origin_enabled: Patch<bool>, |
There was a problem hiding this comment.
It might be clearer to re-export them at the module level if that's possible?
| pub(crate) tracing_sampling_rate: Patch<f64>, | ||
| pub(crate) log_injection_enabled: Patch<bool>, | ||
| pub(crate) tracing_tags: Patch<Vec<String>>, | ||
| pub(crate) tracing_enabled: Patch<bool>, | ||
| pub(crate) tracing_sampling_rules: Patch<Vec<TracingSamplingRule>>, | ||
| pub(crate) dynamic_instrumentation_enabled: Patch<bool>, | ||
| pub(crate) exception_replay_enabled: Patch<bool>, | ||
| pub(crate) code_origin_enabled: Patch<bool>, |
There was a problem hiding this comment.
It might be clearer to re-export them at the module level if that's possible?
| field!(s, tracing_header_tags); | ||
| field!(s, tracing_sampling_rate); | ||
| field!(s, log_injection_enabled); | ||
| field!(s, tracing_tags); | ||
| field!(s, tracing_enabled); | ||
| field!(s, tracing_sampling_rules); | ||
| field!(s, dynamic_instrumentation_enabled); | ||
| field!(s, exception_replay_enabled); | ||
| field!(s, code_origin_enabled); |
There was a problem hiding this comment.
Would it be useful to emit these in a canonical order, e.g. alphabetically sorted by tag?
|
|
||
| impl From<DynamicConfig> for Vec<Configs> { | ||
| fn from(value: DynamicConfig) -> Self { | ||
| let mut vec = vec![]; |
There was a problem hiding this comment.
would it make sense to with_capacity here to reserve space and avoid resizes?
…order Reviewer feedback on PR #2102: - Make DynamicConfig fields pub so callers can read them directly instead of going through Vec<Configs>. - Re-export the dynamic-config types at the config module level so the import path doesn't have to spell out the `dynamic` submodule. - Emit DynamicConfig's manual Serialize impl in alphabetical order so JSON output is deterministic. - Use Vec::with_capacity in From<DynamicConfig> for Vec<Configs> to avoid resizes when all fields are delivered. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8af95e4 to
63f6363
Compare
The earlier `Patch<T>` design assumed JSON Merge Patch semantics for APM_TRACING — three states (absent / null / value), where null carried an explicit "clear this field" signal. The dd-trace-go reference implementation (ddtrace/tracer/remote_config.go) settles otherwise: it uses `*float64` / `*[]rcSamplingRule`, which collapse absent and null into the same `nil`, and the `mergeConfigsByPriority` function only writes when a value is present. Clearing a remote override happens at the file level: when the agent stops delivering a file, the merge re-runs without it and `HandleRC(nil)` reverts to local config. There is no in-file clear signal. Match that contract: - `DynamicConfig` fields are plain `Option<T>` again. `None` covers both "field absent" and "field present as null"; the distinction is not meaningful at this layer. - `Configs` variants carry `T` (not `Option<T>`). A variant is only emitted when the agent delivered a concrete value, so consumers leave any prior remote state untouched if a field is missing. - The `From<DynamicConfig> for Vec<Configs>` impl unwraps with `if let Some(v) = ...`, matching the dd-trace-go merge style. Kept from the prior iteration: - `pub` visibility on the struct fields and the tracing-rule types, so consumers can also access them directly without going through the `Configs` projection. - `#[serde(alias = "tracing_sample_rate")]` on `tracing_sampling_rate` for compatibility with payloads that use the legacy key. - The sidecar's "preserve dynamic_instrumentation across non-DI patches" fix (no upfront `take()`), which is correct regardless of the two- vs three-state question — and is now the *only* mechanism preserving values across incremental updates. Tests in `dynamic.rs` cover the four meaningful cases: absent → no-variant, explicit null → no-variant (indistinguishable from absent), concrete value → set variant, unrelated-field-present → no spurious sampling variants. Plus a guard for the legacy `tracing_sample_rate` alias. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
63f6363 to
51a7d2a
Compare
What does this PR do?
tracing_sample_rateastracing_sampling_rateDynamicConfigproperties