-
Notifications
You must be signed in to change notification settings - Fork 11
chore(antithesis): Bound contexts in runs #2216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: blt/chore_antithesis_alter_dogstatsd_generation_to_is_malformed_regime
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Seeds for failure cases proptest has generated in the past. It is | ||
| # automatically read and these particular cases re-run before any | ||
| # novel cases are generated. | ||
| # | ||
| # It is recommended to check this file in to source control so that | ||
| # everyone who runs the test benefits from these saved cases. | ||
| cc 671f360ca24ca5add047c2f93acf185116ebfa9c663637a430a15fa6abc56151 # shrinks to seed = 8436120490560261333, kind = Metric |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -245,6 +245,9 @@ | |
| /// Upper bound on datagrams one driver invocation ships in a timeline. | ||
| const MAX_DATAGRAMS: usize = 10_000; | ||
|
|
||
| /// Upper bound on the working set one driver invocation fetches from the shared context pool. | ||
| const MAX_WORKING_SET: u64 = 1_024; | ||
|
|
||
| /// Config a load generator reads to shape its output to this timeline's SUT. | ||
| /// `first_sample_config` samples it beside `datadog.yaml` from one draw, so the | ||
| /// generator and the SUT are driven together. | ||
|
|
@@ -256,6 +259,9 @@ | |
| pub payload_byte_limit: usize, | ||
| /// Datagrams a driver invocation ships this timeline. | ||
| pub datagram_count: usize, | ||
| /// Distinct contexts a driver invocation fetches from the shared pool as its working set, each | ||
| /// boundary-biased log-uniform in `1..=MAX_WORKING_SET`. | ||
| pub context_count: usize, | ||
| } | ||
|
|
||
| impl DriverConfig { | ||
|
|
@@ -270,6 +276,7 @@ | |
| Self { | ||
| payload_byte_limit, | ||
| datagram_count: rng.random_range(0..=MAX_DATAGRAMS), | ||
| context_count: usize::try_from(Probe::new(1, MAX_WORKING_SET).sample(rng)).unwrap_or(usize::MAX), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -296,6 +303,77 @@ | |
| } | ||
| } | ||
|
|
||
| /// Upper bound on the distinct contexts a shared pool holds across every kind. The pool retains each | ||
| /// minted context, so the ceiling belongs to the total rather than to any one kind. | ||
| const MAX_CONTEXTS_TOTAL: u64 = 1_000_000; | ||
|
|
||
| /// The per-kind caps a timeline's shared context pool fills to before it recurs existing contexts. | ||
| /// `first_sample_config` samples this beside `datadog.yaml` so cardinality varies per timeline. Each | ||
| /// cap is drawn against the budget the earlier draws left, so a kind's cardinality still varies at | ||
| /// random while the three together stay under [`MAX_CONTEXTS_TOTAL`]. | ||
| #[derive(Clone, Copy, Debug, Serialize, Deserialize)] | ||
| pub struct ContextSourceConfig { | ||
| /// Bytes a rendered line of any pooled context must fit, this timeline's real datagram budget | ||
| /// rather than the protocol ceiling. Mint builds identities against it, so every served context | ||
| /// has a rendering the driver can pack. | ||
| pub payload_byte_limit: usize, | ||
| /// Distinct metric contexts the pool holds. | ||
| pub metric_contexts: usize, | ||
|
Comment on lines
+320
to
+321
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This public YAML field and its two sibling caps only state what they count; they do not document the generated default/range, accepted boundary values, behavioral impact, or guidance for changing them. In particular, AGENTS.md reference: AGENTS.md:L149-L154 Useful? React with 👍 / 👎. |
||
| /// Distinct event contexts the pool holds. | ||
| pub event_contexts: usize, | ||
| /// Distinct service-check contexts the pool holds. | ||
| pub service_check_contexts: usize, | ||
| } | ||
|
|
||
| impl ContextSourceConfig { | ||
| /// Sample the per-kind caps, each boundary-biased log-uniform against the budget still free. | ||
| /// | ||
| /// The draws run in order and each spends from one shared ceiling, so the total is bounded by | ||
| /// construction rather than by scaling three independent draws afterwards. Every kind keeps at | ||
| /// least one context, since a kind capped at zero panics the pool's draw. | ||
| #[must_use] | ||
| pub fn sample<R: Rng + ?Sized>(rng: &mut R, payload_byte_limit: usize) -> Self { | ||
| // Two contexts held back so the later kinds can each keep their one. | ||
| let metric_contexts = sample_cap(rng, MAX_CONTEXTS_TOTAL - 2); | ||
| let free = MAX_CONTEXTS_TOTAL - metric_contexts as u64; | ||
| let event_contexts = sample_cap(rng, free - 1); | ||
| let free = free - event_contexts as u64; | ||
| Self { | ||
| payload_byte_limit, | ||
| metric_contexts, | ||
| event_contexts, | ||
| service_check_contexts: sample_cap(rng, free), | ||
| } | ||
| } | ||
|
|
||
| /// Render `self` as a `context_source.yaml` string. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if serialization fails. | ||
| pub fn to_yaml(&self) -> anyhow::Result<String> { | ||
| serde_yaml::to_string(self).context("serialize context_source.yaml") | ||
| } | ||
|
|
||
| /// Read the context-source config from the `context_source.yaml` that `first_sample_config` wrote | ||
| /// to `config_dir`. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if the config is unreadable or is not valid YAML. | ||
| pub fn read(config_dir: &Path) -> anyhow::Result<Self> { | ||
| let path = config_dir.join("context_source.yaml"); | ||
| let yaml = fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; | ||
| serde_yaml::from_str(&yaml).with_context(|| format!("parse context source config from {}", path.display())) | ||
| } | ||
| } | ||
|
|
||
| /// A single per-kind cap in `1..=ceiling`. The ceiling is well within `usize` on every supported | ||
| /// target, so the saturating conversion is unreachable in practice. | ||
| fn sample_cap<R: Rng + ?Sized>(rng: &mut R, ceiling: u64) -> usize { | ||
| usize::try_from(Probe::new(1, ceiling.max(1)).sample(rng)).unwrap_or(usize::MAX) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use std::collections::BTreeSet; | ||
|
|
@@ -401,6 +479,28 @@ | |
| assert_eq!(seen, [true, true]); | ||
| } | ||
|
|
||
| // The pool holds every minted context, so the ceiling is on the total across kinds rather than on | ||
| // each kind alone. Three independent draws at the ceiling would retain three million. | ||
| #[test] | ||
| fn context_caps_sum_within_the_total_ceiling() { | ||
| for seed in 0..64 { | ||
| let caps = ContextSourceConfig::sample(&mut SeqRng(seed), 8_192); | ||
| let total = (caps.metric_contexts + caps.event_contexts + caps.service_check_contexts) as u64; | ||
| assert!(total <= MAX_CONTEXTS_TOTAL, "seed {seed} sampled {total}"); | ||
| // A kind of zero panics the pool's `random_range(0..0)`, so every kind keeps at least one. | ||
| assert!(caps.metric_contexts >= 1 && caps.event_contexts >= 1 && caps.service_check_contexts >= 1); | ||
| } | ||
| } | ||
|
|
||
| // Randomness still drives each kind rather than the total being split evenly. | ||
| #[test] | ||
| fn context_caps_vary_per_kind() { | ||
| let spread: BTreeSet<usize> = (0..64) | ||
| .map(|seed| ContextSourceConfig::sample(&mut SeqRng(seed), 8_192).metric_contexts) | ||
| .collect(); | ||
| assert!(spread.len() > 8, "metric cap barely varies: {spread:?}"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn compressor_samples_every_kind() { | ||
| let mut seen = BTreeSet::new(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
context_countboundariesWhen a restored or hand-authored
driver.yamlsetscontext_countto0or above65_536,DriverConfig::readaccepts it, but the intake rejects every/contextsrequest; the shared fetch loop then waits for its 30-second budget and both Antithesis drivers return empty stats without generating DogStatsD load. Document the accepted boundaries, invalid-value behavior, and workload-size trade-off here, or validate the field during deserialization, rather than describing only the range produced by the sampler.AGENTS.md reference: AGENTS.md:L149-L154
Useful? React with 👍 / 👎.