Skip to content

chore(config): model duration defaults as a typed value#1966

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 4 commits into
mainfrom
jszwedko/config-classifier-typed-duration-defaults
Jul 2, 2026
Merged

chore(config): model duration defaults as a typed value#1966
gh-worker-dd-mergequeue-cf854d[bot] merged 4 commits into
mainfrom
jszwedko/config-classifier-typed-duration-defaults

Conversation

@jszwedko

@jszwedko jszwedko commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #1958 (now merged), taking the alternative design proposed in review (#1958 (comment)): model format: duration as a first-class type and store parsed duration defaults in the generated classifier data, instead of carrying an is_duration side channel and reparsing a schema string on every classification. The base type is modeled correctly, and an invalid duration default now breaks the build rather than misbehaving at runtime.

Key changes:

  • New go-duration leaf crate owns Go time.ParseDuration parsing. saluki-config's DurationString delegates to it, and the classifier build script uses it too — so the algorithm has a single owner instead of being reimplemented (the parser fix(config): recognize duration keys at their default in the config classifier #1958 added to the classifier is removed). An upstream crate (for example go-parse-duration) can replace this later after vetting; keeping it in-tree for now avoids taking an unvetted external dependency.
  • FieldType::Duration (plus a matching ValueType::Duration for the config-testing smoke tests) replaces the is_duration flag. parse_value_type folds format: duration in and panics on an illogical base type. Smoke tests use a generic "42s" value so future duration keys need no bespoke test_json.
  • Typed DefaultValue (Missing / Json(&'static str) / DurationNanos(u64)) replaces Option<&str> + is_duration on ClassifierEntry. Codegen parses duration defaults to nanoseconds via go-duration and fails the build if a format: duration default isn't a valid Go duration. The runtime default check simply switches on the typed default; it never parses a schema default.

Behavior matches #1958 for the tls_handshake_timeout bug (a default-valued duration is recognized as default whether sent as nanoseconds or a Go duration string), but the model is cleaner and the failure mode moves to compile time.

Test plan

  • go-duration unit tests (Go-style units, signs/fractions/zero, largest value, invalid/overflow, error messages).
  • saluki-config duration tests pass against the delegated parser.
  • Classifier unit tests updated for DefaultValue (per-variant is_default_value coverage; duration matched both as nanoseconds and as a Go duration string).
  • cargo check --workspace --tests and cargo clippy clean; config-testing smoke tests pass.

🤖 Generated with Claude Code

Follow-up to the classifier duration fix that models `format: duration`
as a first-class type instead of an `is_duration` side channel, and
canonicalizes duration defaults at build time rather than reparsing a
schema string on every classification.

- Add a `go-duration` leaf crate that owns Go `time.ParseDuration`
  parsing, and delegate `saluki-config`'s `DurationString` to it so the
  algorithm has a single owner (was duplicated by the classifier fix).
- Fold `format: duration` into `FieldType::Duration` (and a matching
  `ValueType::Duration` for smoke tests), replacing the `is_duration`
  flag. `parse_value_type` panics on an illogical duration base type.
- Replace `ClassifierEntry`'s `Option<&str>` default + `is_duration`
  with a typed `DefaultValue` (`Missing` / `Json` / `DurationNanos`).
  Codegen parses duration defaults to nanoseconds via `go-duration` and
  fails the build if a `format: duration` default isn't a valid Go
  duration; the runtime check just switches on the typed default.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@dd-octo-sts dd-octo-sts Bot added the area/config Configuration. label Jul 1, 2026
@datadog-datadog-prod-us1

This comment has been minimized.

@pr-commenter

pr-commenter Bot commented Jul 1, 2026

Copy link
Copy Markdown

Binary Size Analysis (Agent Data Plane)

Baseline: 4de556d · Comparison: cbd376b · diff
Analysis Configuration: stripped binaries · Pass/Fail Threshold: +5%
Sizes: 41.63 MiB (baseline) vs 41.63 MiB (comparison)
Size Change: +3.74 KiB (+0.01%)

✅ Binary size difference within threshold

Changes by Module
Module File Size Symbols
anon.3c69ceabc60ce31e47fb49ebd37b949d.38.llvm.8520628709667317489 +7.95 KiB 1
anon.5043bac4e9f013c95061f55d54848406.113.llvm.4850616680965394514 -7.94 KiB 1
anon.058104e7c5e17815cc397c51159e7bb6.17.llvm.5917434062816465002 +7.47 KiB 1
anon.eb8a6db73655bdd84768962c5db517d1.6.llvm.980884025277214083 -7.46 KiB 1
anon.264f64749ed47b9540def0041d7d333b.77.llvm.1147429972880098824 +6.63 KiB 1
anon.0dc1de58c4eb14eb4a57fa004575eaa2.84.llvm.8144985070982893752 -6.32 KiB 1
anon.122d08581a1227880b36542f3c021e03.7.llvm.16111821345713007611 +4.16 KiB 1
anon.a8b821c4662cac6111d92383c4c8e97b.40.llvm.975721454091953032 -4.16 KiB 1
[Unmapped] +4.06 KiB 1
go_duration::parse_duration +4.06 KiB 1
saluki_config::duration_string::parse_duration -4.04 KiB 1
anon.f05579d214bd720560ee0207fdeaa44b.110.llvm.10277623636693697842 +3.89 KiB 1
anon.5630fd79b5761ba0fc7e0b37ede0e883.119.llvm.15480722195153566423 -3.88 KiB 1
serde_core +3.53 KiB 1049
crossbeam_channel -3.53 KiB 160
anon.197e39290976d77f6c512e0753380664.297.llvm.15470508063503015492 -3.37 KiB 1
anon.60bec9cfa65cbbc713774540ce9fa13a.297.llvm.5315471928682786756 +3.37 KiB 1
anon.c2ceec372c14f40e2ac6ec6badd818f6.5.llvm.15452598339203213699 +3.05 KiB 1
anon.3de8e9c07c905cbdb0efe6bb8c0c345e.0.llvm.1672929009223888530 -3.04 KiB 1
anon.12be1b11b7a076fc406c0946a6016761.359.llvm.12380805039573619983 +2.84 KiB 1
Detailed Symbol Changes
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  [NEW] +56.8Ki  [NEW] +56.6Ki    saluki_components::common::datadog::io::run_endpoint_io_loop::_{{closure}}::h54fbb73bb30184e6
  [NEW] +44.7Ki  [NEW] +44.6Ki    agent_data_plane::cli::run::handle_run_command::_{{closure}}::h83501a7881036c5f
  [NEW] +38.7Ki  [NEW] +38.5Ki    _<saluki_components::forwarders::otlp::OtlpForwarder as saluki_core::components::forwarders::Forwarder>::run::_{{closure}}::h3cd6ee67f0471508
  [NEW] +32.6Ki  [NEW] +32.4Ki    _<saluki_components::transforms::aggregate::Aggregate as saluki_core::components::transforms::Transform>::run::_{{closure}}::h72e4b09fd816d94f
  [NEW] +28.6Ki  [NEW] +28.5Ki    agent_data_plane::cli::dogstatsd::handle_dogstatsd_command::_{{closure}}::h84db3f7ad161eab2
  [NEW] +28.6Ki  [NEW] +28.5Ki    saluki_components::sources::otlp::metrics::translator::OtlpMetricsTranslator::translate_metrics::h1e8351a6f731e9df
  [NEW] +27.2Ki  [NEW] +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
  [NEW] +26.6Ki  [NEW] +26.4Ki    _<saluki_components::sources::dogstatsd::DogStatsDConfiguration as saluki_core::components::sources::builder::SourceBuilder>::build::_{{closure}}::h2c2a33d551977c6d
  [NEW] +25.6Ki  [NEW] +25.4Ki    saluki_components::sources::dogstatsd::drive_stream::_{{closure}}::h3fe2520e3abdfc38
  [NEW] +25.0Ki  [NEW] +24.8Ki    agent_data_plane::internal::remote_agent::run_remote_agent_registration_loop::_{{closure}}::h64859e60eb17f920
  +0.0% +3.45Ki  -0.0%    -354    [44936 Others]
  [DEL] -25.0Ki  [DEL] -24.8Ki    agent_data_plane::internal::remote_agent::run_remote_agent_registration_loop::_{{closure}}::h4e2e551f8139db38
  [DEL] -25.6Ki  [DEL] -25.5Ki    saluki_components::sources::dogstatsd::drive_stream::_{{closure}}::hc4388ae93a328f58
  [DEL] -26.5Ki  [DEL] -26.3Ki    _<saluki_components::sources::dogstatsd::DogStatsDConfiguration as saluki_core::components::sources::builder::SourceBuilder>::build::_{{closure}}::h9b7f1b7f5ed53de7
  [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::hf9734c02c96e53ea
  [DEL] -28.6Ki  [DEL] -28.4Ki    saluki_components::sources::otlp::metrics::translator::OtlpMetricsTranslator::translate_metrics::h762652f9497ab51c
  [DEL] -28.6Ki  [DEL] -28.5Ki    agent_data_plane::cli::dogstatsd::handle_dogstatsd_command::_{{closure}}::h20260c826fa2e80d
  [DEL] -32.5Ki  [DEL] -32.3Ki    _<saluki_components::transforms::aggregate::Aggregate as saluki_core::components::transforms::Transform>::run::_{{closure}}::h5255c5fdf9a6142d
  [DEL] -38.7Ki  [DEL] -38.5Ki    _<saluki_components::forwarders::otlp::OtlpForwarder as saluki_core::components::forwarders::Forwarder>::run::_{{closure}}::hf061731a7e18b6ce
  [DEL] -44.7Ki  [DEL] -44.6Ki    agent_data_plane::cli::run::handle_run_command::_{{closure}}::hc38d95056d131633
  [DEL] -56.6Ki  [DEL] -56.4Ki    saluki_components::common::datadog::io::run_endpoint_io_loop::_{{closure}}::h576e42a88b0e33a6
  +0.0% +3.74Ki  -0.0%     -60    TOTAL

@jszwedko jszwedko changed the title refactor(config): model duration defaults as a typed value chore(config): model duration defaults as a typed value Jul 1, 2026
@pr-commenter

pr-commenter Bot commented Jul 1, 2026

Copy link
Copy Markdown

Regression Detector (Agent Data Plane)

Run ID: ea0a57da-336a-4219-b958-ff1b83710556
Baseline: 4de556d0 · Comparison: cbd376be · diff

Optimization Goals: ✅ No significant changes detected

Fine details of change detection per experiment (5)

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_medium memory ⚪ +0.03 metrics profiles logs
quality_gates_rss_idle memory ⚪ +0.01 metrics profiles logs
quality_gates_rss_dsd_ultraheavy memory ⚪ -0.03 metrics profiles logs
quality_gates_rss_dsd_heavy memory ⚪ -0.06 metrics profiles logs
quality_gates_rss_dsd_low memory ⚪ -0.11 metrics profiles logs
Bounds Checks: ✅ Passed (5)
experiment check replicates observed links
quality_gates_rss_dsd_heavy memory_usage 10/10 ✅ 131 MiB ≤ 140 MiB metrics profiles logs
quality_gates_rss_dsd_low memory_usage 10/10 ✅ 42.9 MiB ≤ 50 MiB metrics profiles logs
quality_gates_rss_dsd_medium memory_usage 10/10 ✅ 64.8 MiB ≤ 75 MiB metrics profiles logs
quality_gates_rss_dsd_ultraheavy memory_usage 10/10 ✅ 191 MiB ≤ 200 MiB metrics profiles logs
quality_gates_rss_idle memory_usage 10/10 ✅ 29 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.

Reword doc comments flagged by check-docs: drop spaced em-dashes and
avoid dictionary-rejected words (canonicalized/reparses/reimplemented).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

@webern webern left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on a quick pass and does what I think I need on my branch(es) 👍

Comment thread lib/datadog-agent/config-overlay-model/src/schema_gen.rs Outdated
@jszwedko jszwedko marked this pull request as ready for review July 2, 2026 22:01
@jszwedko jszwedko requested a review from a team as a code owner July 2, 2026 22:01
Merged main's advancement, including removal of dogstatsd_pipe_name and
dogstatsd_windows_pipe_security_descriptor keys from the overlay. Regenerated
classifier_data.rs from the merged overlay schema.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit 7b98c6f into main Jul 2, 2026
85 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the jszwedko/config-classifier-typed-duration-defaults branch July 2, 2026 23:28
dd-octo-sts Bot pushed a commit that referenced this pull request Jul 2, 2026
## Summary

Follow-up to #1958 (now merged), taking the alternative design proposed in review (#1958 (comment)): model `format: duration` as a first-class type and store **parsed** duration defaults in the generated classifier data, instead of carrying an `is_duration` side channel and reparsing a schema string on every classification. The base type is modeled correctly, and an invalid duration default now breaks the build rather than misbehaving at runtime.

Key changes:

- **New `go-duration` leaf crate** owns Go `time.ParseDuration` parsing. `saluki-config`'s `DurationString` delegates to it, and the classifier build script uses it too — so the algorithm has a single owner instead of being reimplemented (the parser #1958 added to the classifier is removed). An upstream crate (for example `go-parse-duration`) can replace this later after vetting; keeping it in-tree for now avoids taking an unvetted external dependency.
- **`FieldType::Duration`** (plus a matching `ValueType::Duration` for the config-testing smoke tests) replaces the `is_duration` flag. `parse_value_type` folds `format: duration` in and panics on an illogical base type. Smoke tests use a generic `"42s"` value so future duration keys need no bespoke `test_json`.
- **Typed `DefaultValue`** (`Missing` / `Json(&'static str)` / `DurationNanos(u64)`) replaces `Option<&str>` + `is_duration` on `ClassifierEntry`. Codegen parses duration defaults to nanoseconds via `go-duration` and **fails the build** if a `format: duration` default isn't a valid Go duration. The runtime default check simply switches on the typed default; it never parses a schema default.

Behavior matches #1958 for the `tls_handshake_timeout` bug (a default-valued duration is recognized as default whether sent as nanoseconds or a Go duration string), but the model is cleaner and the failure mode moves to compile time.

## Test plan

- [x] `go-duration` unit tests (Go-style units, signs/fractions/zero, largest value, invalid/overflow, error messages).
- [x] `saluki-config` duration tests pass against the delegated parser.
- [x] Classifier unit tests updated for `DefaultValue` (per-variant `is_default_value` coverage; duration matched both as nanoseconds and as a Go duration string).
- [x] `cargo check --workspace --tests` and `cargo clippy` clean; config-testing smoke tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: jesse.szwedko <jesse.szwedko@datadoghq.com> 7b98c6f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants