Skip to content

chore(config): migrate DatadogEventsConfiguration to typed config#1981

Merged
webern merged 2 commits into
m/pr5-cutoverfrom
m/datadog-events
Jul 4, 2026
Merged

chore(config): migrate DatadogEventsConfiguration to typed config#1981
webern merged 2 commits into
m/pr5-cutoverfrom
m/datadog-events

Conversation

@webern

@webern webern commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

AI Summary

Build DatadogEventsConfiguration from the typed shared configuration model instead of deserializing the generic configuration map. Remove component-local source defaults and retire the configuration smoke test.

Change Type

  • Non-functional (chore, refactoring, docs)

How did you test this PR?

  • make build-schema-overlay && make fmt
  • make check-fmt check-docs
  • make test
  • make check-deny check-unused-deps check-licenses check-features generate-api-docs
  • cargo check -p saluki-components -p agent-data-plane

References

No issue.

@webern webern requested a review from a team as a code owner July 4, 2026 13:48
Copilot AI review requested due to automatic review settings July 4, 2026 13:48
@dd-octo-sts dd-octo-sts Bot added area/components Sources, transforms, and destinations. encoder/datadog-events Datadog events encoder. labels Jul 4, 2026
@datadog-official

This comment has been minimized.

Copilot AI 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.

Pull request overview

Refactors the Datadog events encoder configuration flow so DatadogEventsConfiguration is constructed from the typed shared configuration model (agent_data_plane_config) instead of deserializing from the generic config map, and updates the baseline events pipeline wiring accordingly.

Changes:

  • Build DatadogEventsConfiguration from typed shared config (MetricsEncoding + Compression) rather than GenericConfiguration.
  • Remove component-local serde defaults / deserialization path for the events encoder config.
  • Replace the prior config smoke test with a focused unit test for the new typed-config constructor.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
lib/saluki-components/src/encoders/datadog/events/mod.rs Switches events encoder config construction to typed shared config and replaces config smoke testing with a unit test.
bin/agent-data-plane/src/cli/run.rs Updates baseline events pipeline setup to use ConfigurationSystem (typed config) when creating the events encoder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 42 to 46
/// Maximum compressed size, in bytes, of an events payload.
///
/// This uses the same generic event payload setting as the Datadog Agent. ADP sends events to
/// `/api/v1/events_batch`, so the effective value is clamped to that endpoint's global intake limit of 3,200,000
/// bytes. If set to `0`, every non-empty compressed payload exceeds the limit and is dropped during flush.
Comment on lines 49 to 53
/// Maximum uncompressed size, in bytes, of an events payload.
///
/// This uses the same generic event payload setting as the Datadog Agent. ADP sends events to
/// `/api/v1/events_batch`, so the effective value is clamped to that endpoint's global intake limit of 62,914,560
/// bytes. Values smaller than the minimum endpoint framing size prevent the request builder from starting.
Comment on lines 56 to 61
/// Compression kind to use for the request payloads.
///
/// Defaults to `zstd`.
#[serde(
rename = "serializer_compressor_kind",
default = "default_serializer_compressor_kind"
)]
compressor_kind: String,

/// Compressor level to use when the compressor kind is `zstd`.
///
/// Defaults to 3.
#[serde(
rename = "serializer_zstd_compressor_level",
default = "default_zstd_compressor_level"
)]
zstd_compressor_level: i32,

Comment on lines 62 to 64
/// Whether to log event payload contents before encoding.
///
/// This logs decoded event objects, not the encoded HTTP body.

@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: f6b9a232e9

ℹ️ 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".

max_payload_size: metrics_encoding.max_payload_size,
max_uncompressed_payload_size: metrics_encoding.max_uncompressed_payload_size,
compressor_kind: compression.compressor_kind.clone(),
zstd_compressor_level: compression.zstd_compressor_level,

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 Preserve ADP zstd level default for events

When the events pipeline is enabled and serializer_zstd_compressor_level is omitted, this now copies the typed Datadog default of 1; the project overlay explicitly documents ADP's intentional default divergence as level 3 (lib/datadog-agent/config/schema/schema_overlay.yaml:2253-2257), and the removed component default also returned 3. That makes this refactor change default event compression behavior, producing larger payloads/more splits and potentially dropping events near the compressed limit, so the migration needs to preserve the ADP default when the key is unset.

Useful? React with 👍 / 👎.

@pr-commenter

pr-commenter Bot commented Jul 4, 2026

Copy link
Copy Markdown

Binary Size Analysis (Agent Data Plane)

Baseline: 7b98c6f · Comparison: f41a555 · diff
Analysis Configuration: stripped binaries · Pass/Fail Threshold: +5%
Sizes: 41.63 MiB (baseline) vs 42.52 MiB (comparison)
Size Change: +905.49 KiB (+2.12%)

✅ Binary size difference within threshold

Changes by Module
Module File Size Symbols
serde_json +230.34 KiB 512
core +185.24 KiB 9375
datadog_agent_config::generated::datadog_configuration +112.84 KiB 54
saluki_components::common::datadog +71.53 KiB 574
serde_core +60.41 KiB 967
alloc +55.44 KiB 1687
figment -54.15 KiB 589
[sections] +49.34 KiB 8
serde -41.20 KiB 81
tokio +37.75 KiB 3178
agent_data_plane_config_system::translators::datadog_translator +32.23 KiB 19
h2 -22.26 KiB 449
hashbrown -19.80 KiB 852
prost +17.54 KiB 292
saluki_common::task::instrument -17.49 KiB 35
anon.4fca03d374364d60d4850bcc82c9cbb5.0.llvm.3580026879976834362 +17.41 KiB 1
anon.4971a138abcbf76d70dd2f0a87a5dffb.52.llvm.8870522310722751024 -16.97 KiB 1
tracing +16.73 KiB 118
resource_accounting::groups::Tracked +15.09 KiB 35
quick_cache -13.91 KiB 97
Detailed Symbol Changes
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +4.6%  +798Ki  +4.6%  +613Ki    [35511 Others]
  [NEW] +55.0Ki  [NEW] +54.8Ki    saluki_components::common::datadog::io::run_endpoint_io_loop::_{{closure}}::h3ef2a484186f0bb8
  [NEW] +52.6Ki  [NEW] +52.2Ki    _<datadog_agent_config::generated::datadog_configuration::_::<impl serde_core::de::Deserialize for datadog_agent_config::generated::datadog_configuration::DatadogConfiguration>::deserialize::__Visitor as serde_core::de::Visitor>::visit_map::h59abc0a47db21533
  [NEW] +45.9Ki  [NEW] +45.8Ki    agent_data_plane::cli::run::handle_run_command::_{{closure}}::he5819430e26508ec
  [NEW] +38.7Ki  [NEW] +38.5Ki    _<saluki_components::forwarders::otlp::OtlpForwarder as saluki_core::components::forwarders::Forwarder>::run::_{{closure}}::h3d7ef516f362b9a8
  [NEW] +37.8Ki  [NEW] +37.6Ki    _<saluki_components::transforms::apm_stats::ApmStats as saluki_core::components::transforms::Transform>::run::_{{closure}}::h25d39d673bf8c8a4
  [NEW] +37.0Ki  [NEW] +36.7Ki    _<saluki_components::common::datadog::obfuscation::_::<impl serde_core::de::Deserialize for saluki_components::common::datadog::obfuscation::ObfuscationConfig>::deserialize::__Visitor as serde_core::de::Visitor>::visit_map::h5dc7893c0984844c
  [NEW] +33.0Ki  [NEW] +32.9Ki    saluki_components::sources::dogstatsd::drive_stream::_{{closure}}::h16acca5cc1c6ba60
  [NEW] +30.3Ki  [NEW] +30.1Ki    _<saluki_components::transforms::aggregate::Aggregate as saluki_core::components::transforms::Transform>::run::_{{closure}}::h6809d072204e0804
  [NEW] +29.7Ki  [NEW] +29.6Ki    agent_data_plane::cli::dogstatsd::handle_dogstatsd_command::_{{closure}}::hd22ba2289c2b0af8
  +2.6% +29.2Ki  +2.6% +29.2Ki    [section .gcc_except_table]
  [NEW] +27.6Ki  [NEW] +27.4Ki    _<saluki_components::sources::dogstatsd::DogStatsDConfiguration as saluki_core::components::sources::builder::SourceBuilder>::build::_{{closure}}::h6b9140c89e43d005
  [DEL] -25.6Ki  [DEL] -25.4Ki    saluki_components::sources::dogstatsd::drive_stream::_{{closure}}::h3fe2520e3abdfc38
  [DEL] -26.6Ki  [DEL] -26.4Ki    _<saluki_components::sources::dogstatsd::DogStatsDConfiguration as saluki_core::components::sources::builder::SourceBuilder>::build::_{{closure}}::h2c2a33d551977c6d
  [DEL] -27.2Ki  [DEL] -26.9Ki    _<saluki_components::sources::dogstatsd::_::<impl serde_core::de::Deserialize for saluki_components::sources::dogstatsd::DogStatsDConfiguration>::deserialize::__Visitor as serde_core::de::Visitor>::visit_map::hfc578ba92a99992a
  [DEL] -28.6Ki  [DEL] -28.5Ki    saluki_components::sources::otlp::metrics::translator::OtlpMetricsTranslator::translate_metrics::h1e8351a6f731e9df
  [DEL] -28.6Ki  [DEL] -28.5Ki    agent_data_plane::cli::dogstatsd::handle_dogstatsd_command::_{{closure}}::h84db3f7ad161eab2
  [DEL] -32.6Ki  [DEL] -32.4Ki    _<saluki_components::transforms::aggregate::Aggregate as saluki_core::components::transforms::Transform>::run::_{{closure}}::h72e4b09fd816d94f
  [DEL] -38.7Ki  [DEL] -38.5Ki    _<saluki_components::forwarders::otlp::OtlpForwarder as saluki_core::components::forwarders::Forwarder>::run::_{{closure}}::h3cd6ee67f0471508
  [DEL] -44.7Ki  [DEL] -44.6Ki    agent_data_plane::cli::run::handle_run_command::_{{closure}}::h83501a7881036c5f
  [DEL] -56.8Ki  [DEL] -56.6Ki    saluki_components::common::datadog::io::run_endpoint_io_loop::_{{closure}}::h54fbb73bb30184e6
  +2.1%  +905Ki  +2.1%  +720Ki    TOTAL

Build the events encoder from the shared SalukiConfiguration model and remove source-map deserialization from the component.
@webern webern force-pushed the m/datadog-events branch from f6b9a23 to f41a555 Compare July 4, 2026 14:04
@webern

webern commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Clean-room review — Claude Code operating on behalf of @webern.

Blocking: zstd_compressor_level default regresses from 3 to 1

This cutover removes the component-local default_zstd_compressor_level() (which returned 3) and instead reads zstd_compressor_level from shared.endpoints.compression. That field is populated from the generated DatadogConfiguration, whose default is 1 (serializer_zstd_compressor_level: defaults::default_u64::<i64, 1>()).

The generated 1 is the upstream Agent default, but ADP intentionally diverges. The schema overlay itself documents this:

serializer_zstd_compressor_level — "ADP defaults to level 3 vs Agent level 1. ADP intentionally compresses better. Key is recognized by both; default divergence is intentional."

The overlay only records this in prose; there is no default/default_override mechanism carrying it into the generated config, so the typed layer currently defaults to 1. Net effect: when serializer_zstd_compressor_level is unset, event payloads now compress at level 1 instead of 3 — larger payloads, more splits, and a higher chance of dropping events near the compressed limit. It also leaves events inconsistent with the still-unmigrated metrics/service-checks/logs encoders, which continue to default to 3.

Fix belongs in the config layer (that's where the default should live): make the typed default for serializer_zstd_compressor_level reflect ADP's intended 3, then this component can keep reading shared.endpoints.compression.zstd_compressor_level transparently. Codex flagged the same issue.

Everything else checks out

  • Input sanitization — no regression. The translator clamps negative sizes (value.max(0)), and clamp_payload_limits(...) still runs in build().
  • Reachability — no regression. serializer_max_payload_size, serializer_max_uncompressed_payload_size, serializer_compressor_kind, and log_payloads all still flow through the translator into the shared model and are read here.
  • Other defaults — match exactly: max_payload_size 2621440, max_uncompressed_payload_size 4194304, compressor_kind zstd, log_payloads false.
  • Test loss — acceptable. The removed smoke test was a deserialization/registry artifact; the new from_configuration_reads_typed_shared_values test covers the field mapping.
  • Doc nits — Copilot's suggestion to keep documenting each field's default/source key is reasonable and worth a quick pass while fixing the zstd default.

Once the zstd default is carried by the config layer, this is a clean, transparent lift.

@webern

webern commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my earlier comment — Claude Code operating on behalf of @webern.

I need to retract the "blocking" framing on the zstd_compressor_level default. After tracing the actual runtime config path, there is no production regression.

In the default deployment (data_plane.use_new_config_stream_endpoint, which defaults to true), ADP treats the Datadog Agent's streamed configuration as authoritative. The Agent builds each snapshot from AllFlattenedSettingsWithSequenceID(), which enumerates every registered key and emits its resolved value — i.e. the default when unset. serializer_zstd_compressor_level is a registered key with Agent default 1, so the snapshot always carries serializer_zstd_compressor_level: 1.

That means the old component code path already saw 1: config.as_typed::<DatadogEventsConfiguration>() deserialized a map in which the key was present, so the component's #[serde(default = "default_zstd_compressor_level")] (which returned 3) never fired. Events has effectively been compressing at level 1 in bundled deployments all along. This PR reading 1 from the typed layer preserves that behavior exactly.

The only case that changes is standalone mode (no Agent config stream): there the config is just the local file + env, so an unset key was previously absent and fell back to the component default 3, whereas it now resolves to the typed-layer default 1. That shift actually brings standalone in line with what a real Agent supplies, so I don't consider it a regression worth blocking on.

Net: this cutover is behavior-preserving where it matters. The one real follow-up is documentation, not code — the schema overlay's note for serializer_zstd_compressor_level states ADP "intentionally defaults to level 3," but that intent never took effect in production (the Agent's 1 always shadowed the component default). Worth reconciling that doc with reality separately; it shouldn't hold up this PR.

Apologies for the noise on the first pass.

Copilot AI review requested due to automatic review settings July 4, 2026 14:30
@pr-commenter

pr-commenter Bot commented Jul 4, 2026

Copy link
Copy Markdown

Regression Detector (Agent Data Plane)

Run ID: 126ef8c7-10a3-458b-bfe0-0e13a10b1ef2
Baseline: 7b98c6f1 · Comparison: f41a5551 · diff

Optimization Goals: ✅ No significant changes detected

Fine details of change detection per experiment (3)

Experiments configured erratic: true are tagged (ignored) and skipped when determining which experiments regressed or improved. Experiments which are detected as erratic at runtime are tagged (erratic) to flag that the run's sample dispersion was high, but their regression / improvement signal still counts.

experiment goal Δ mean % links
quality_gates_rss_dsd_low memory ⚪ +2.22 metrics profiles logs
quality_gates_rss_idle memory ⚪ +1.77 metrics profiles logs
quality_gates_rss_dsd_medium memory ⚪ +1.57 metrics profiles logs
Bounds Checks: ✅ Passed (3)
experiment check replicates observed links
quality_gates_rss_dsd_low memory_usage 10/10 ✅ 43.9 MiB ≤ 50 MiB metrics profiles logs
quality_gates_rss_dsd_medium memory_usage 10/10 ✅ 65.6 MiB ≤ 75 MiB metrics profiles logs
quality_gates_rss_idle memory_usage 10/10 ✅ 29.5 MiB ≤ 40 MiB metrics profiles logs
Explanation

A change is flagged as a regression when |Δ mean %| > 5.00% in the regressing direction for its optimization goal AND SMP marks the experiment as a regression (is_regression: true). Improvements use the matching criteria for the improving direction. Experiments configured erratic: true (tagged (ignored)) are skipped outright; experiments detected as erratic at runtime (tagged (erratic)) still count, since that flag describes sample dispersion rather than directional certainty. The Δ mean % cell is colored accordingly: 🟢 = improvement, 🔴 = regression, ⚪ = neutral. Reduction in CPU or memory is an improvement; reduction in ingress throughput is a regression.

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

// operator who deliberately set `1` apart from the Agent's default `1`, so both are raised to
// the floor. Follow-up work needs to track each value's source (user-set vs Agent default) so
// we can honor an explicit low level while still applying our floor to the Agent default.
self.config.shared.endpoints.compression.zstd_compressor_level = (value as i32).max(ZSTD_MINIMUM);
Comment on lines +69 to +73
/// Creates a new `DatadogEventsConfiguration` from the shared typed configuration.
pub fn from_configuration(
metrics_encoding: &MetricsEncoding, compression: &Compression,
) -> Result<Self, GenericError> {
Ok(Self {
Ok(config.as_typed()?)
/// Creates a new `DatadogEventsConfiguration` from the shared typed configuration.
pub fn from_configuration(
metrics_encoding: &MetricsEncoding, compression: &Compression,
@webern webern force-pushed the m/datadog-events branch from 6413191 to 0d6ccb7 Compare July 4, 2026 14:34
The core Agent streams a fully resolved config, so its schema default for
serializer_zstd_compressor_level arrives as a concrete value that would
otherwise overwrite ADP's intended level. Add a ZSTD_DEFAULT_OVERRIDE const in
the config layer and, when the incoming level equals the Agent's schema default,
substitute it, preserving ADP's harder-compression behavior. Any other value is
treated as an explicit operator choice and left untouched.
Copilot AI review requested due to automatic review settings July 4, 2026 14:38
@webern webern force-pushed the m/datadog-events branch from 0d6ccb7 to 3b93a36 Compare July 4, 2026 14:38
@webern webern merged commit 8d48c4e into m/pr5-cutover Jul 4, 2026
22 of 43 checks passed
@webern webern deleted the m/datadog-events branch July 4, 2026 14:40

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines +70 to 81
pub fn from_configuration(
metrics_encoding: &MetricsEncoding, compression: &Compression,
) -> Result<Self, GenericError> {
Ok(Self {
max_payload_size: metrics_encoding.max_payload_size,
max_uncompressed_payload_size: metrics_encoding.max_uncompressed_payload_size,
compressor_kind: compression.compressor_kind.clone(),
zstd_compressor_level: compression.zstd_compressor_level,
log_payloads: metrics_encoding.log_payloads,
})
}
}
Comment on lines 954 to +967
fn consume_serializer_zstd_compressor_level(&mut self, value: i64) {
self.config.shared.endpoints.compression.zstd_compressor_level = value as i32;
// TODO: The core Agent streams a fully resolved config, so its schema default for
// `serializer_zstd_compressor_level` arrives here as a concrete value rather than being
// absent. When the incoming level matches that Agent default we swap in ADP's intended
// level; without this the Agent default would silently override it. We compare against the
// schema-generated default, so an operator who deliberately sets exactly the Agent default is
// indistinguishable from the default itself and also gets overridden. Removing that ambiguity
// needs per-value source tracking (user-set vs Agent default), which is follow-up work.
let agent_default = DatadogConfiguration::default().serializer_zstd_compressor_level;
self.config.shared.endpoints.compression.zstd_compressor_level = if value == agent_default {
ZSTD_DEFAULT_OVERRIDE
} else {
value as i32
};
webern added a commit that referenced this pull request Jul 5, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

No issue.
webern added a commit that referenced this pull request Jul 5, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

No issue.
webern added a commit that referenced this pull request Jul 5, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

No issue.
webern added a commit that referenced this pull request Jul 5, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

No issue.
webern added a commit that referenced this pull request Jul 5, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

No issue.
webern added a commit that referenced this pull request Jul 8, 2026
)

## AI Summary

Build `DatadogEventsConfiguration` from the typed shared configuration
model instead of deserializing the generic configuration map. Remove
component-local source defaults and retire the configuration smoke test.

## Change Type
- [x] Non-functional (chore, refactoring, docs)

## How did you test this PR?

- `make build-schema-overlay && make fmt`
- `make check-fmt check-docs`
- `make test`
- `make check-deny check-unused-deps check-licenses check-features
generate-api-docs`
- `cargo check -p saluki-components -p agent-data-plane`

## References

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

Labels

area/components Sources, transforms, and destinations. encoder/datadog-events Datadog events encoder.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants