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 5 commits into
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 74dc659 | 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
Named constructors (`Patch::absent`/`clear`/`set`) make construction sites self-documenting. The `tracing_sample_rate` alias keeps payloads from legacy producers from being silently dropped as absent after the field rename. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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".
| for config in configs { | ||
| if let Configs::DynamicInstrumentationEnabled(enabled) = config { | ||
| writer.dynamic_instrumentation = Some(enabled); | ||
| writer.dynamic_instrumentation = enabled; |
There was a problem hiding this comment.
Preserve dynamic instrumentation when the field is absent
When an APM_TRACING update omits dynamic_instrumentation_enabled (for example it only changes tracing_tags), Vec<Configs> now correctly emits no DynamicInstrumentationEnabled variant, but writer.dynamic_instrumentation was already cleared by the take() above this loop. Since this assignment only runs for delivered variants, the previous remote override is lost and the sidecar can unforce LiveDebugger even though the new patch did not clear the field; keep the old value unless an explicit Some(...)/None patch is present.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
umm, It seems a problem not related with the changes introduced by this PR. I'm going to change it.
wdyt @bwoebi?
…ING patches The shm_remote_config fetched() handler was clearing writer.dynamic_instrumentation up front and rebuilding it only from delivered Configs variants. With JSON Merge Patch semantics, a payload that updates an unrelated field (e.g. tracing_tags) omits dynamic_instrumentation_enabled to mean "no change" — but the take() collapsed that into "clear", triggering a spurious LiveDebugger unforce/force cycle. Keep the prior value and only mutate when a DynamicInstrumentationEnabled variant is actually delivered. Some(_) sets, explicit None (now distinguishable thanks to Patch<T>) clears, absent leaves the value alone. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
| 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?
What does this PR do?
tracing_sample_rateastracing_sampling_ratePatch<T>newtype that encodesJSON Merge Patch (RFC 7396) semantics:
Configsvariants now carryOption<T>(Some = set, None = clear), andFrom<DynamicConfig> for Vec<Configs>only emits a variant for fieldsthat 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 updatedunrelated fields (e.g. tracing_tags) would wipe an active remote
sample-rate override.
Notes