enhancement(transforms): dynamic rate for sample#25035
enhancement(transforms): dynamic rate for sample#25035jh7459-gh wants to merge 6 commits intovectordotdev:masterfrom
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
22 similar comments
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
|
recheck |
This comment was marked as spam.
This comment was marked as spam.
14 similar comments
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
| self.increment_dynamic_rate(rate, group_by_key, value) | ||
| } | ||
| None => self.static_mode.increment(group_by_key, value), | ||
| }; |
There was a problem hiding this comment.
Take the following with a grain of salt since it is AI generated but I do think we should make this stateless. Free free to discuss more in this thread.
Dynamic state is stale when per-event value changes across a group bucket
The counter-based accumulators (dynamic_values, dynamic_counters) are the root cause of the stale-state bug — each event's outcome is tied
to the history of previous events in the same bucket. For a truly per-event dynamic path, don't accumulate anything. Each event should be
decided independently.
The two cases
-
When
key_fieldis set: already stateless — the hash of the key is compared against the threshold. No change needed. -
When
key_fieldis NOT set: currently uses the accumulator. Replace withrand::random:
fn sample_with_dynamic_ratio(ratio: f64, value: Option<&Value>) -> bool {
match value {
Some(v) => SampleMode::hash_with_ratio(v.to_string_lossy().as_bytes(), ratio),
None => rand::random::<f64>() < ratio,
}
}
fn sample_with_dynamic_rate(rate: u64, value: Option<&Value>) -> bool {
match value {
Some(v) => seahash::hash(v.to_string_lossy().as_bytes()).is_multiple_of(rate),
None => rand::random::<f64>() < (1.0 / rate as f64),
}
}These become plain associated fn with no &mut self and no group_by_key parameter.
What to remove
- dynamic_values: HashMap<Option, f64> — delete from struct
- dynamic_counters: HashMap<Option, u64> — delete from struct
- The HashMap::default() initializers in new_with_dynamic
- rand is already a workspace dependency so no new dep needed
Updated call site in transform()
let should_sample = match event_sample_mode {
Some(EventSampleMode::Ratio(ratio)) => Self::sample_with_dynamic_ratio(ratio, value),
Some(EventSampleMode::Rate(rate)) => Self::sample_with_dynamic_rate(rate, value),
None => self.static_mode.increment(group_by_key, value),
};group_by_key is now only passed to the static path. &mut self on transform is still needed for the static path's counters.
Trade-off
The counter-based approach guaranteed exactly the configured ratio over a window (e.g. exactly 1-in-10 for ratio=0.1). Random sampling gives the
correct ratio in expectation but with variance — noticeable on low-traffic streams. If deterministic throughput matters for the dynamic path,
the alternative fix is to key state by (group_by_key, ratio_or_rate) instead of just group_by_key. But stateless random is simpler and
consistent with how key_field-based sampling already works.
There was a problem hiding this comment.
👍
removed dynamic accumulator, made dynamic sampling stateless for keyless events (rand::random)
keeps hashed behavior when a key value is present
| /// static sampling settings (`rate` or `ratio`) are used as a fallback. | ||
| /// This option cannot be used together with `ratio_field`. | ||
| #[configurable(metadata(docs::examples = "sample_rate_n"))] | ||
| pub rate_field: Option<String>, |
There was a problem hiding this comment.
With dynamic fields, the hash for field_key key is checked against a different threshold per event (driven by ratio_field/rate_field). Two events sharing a key_field value but with different dynamic values can now be split, silently violating the existing contract.
Something to consider:
- Document that
key_fieldonly maintains its coherence guarantee when the dynamic field value is constant within that key - or reject the combination at config-build time.
There was a problem hiding this comment.
👍
chose the strict option
config now rejects key_field with ratio_field/rate_field + explicit validation error
|
recheck |
|
I have read the CLA Document and I hereby sign the CLA |
|
recheck |
Summary
This PR improves the
sampletransform for per-event sampling use cases by adding dynamic field-driven sampling while preserving safe fallback behavior.Changes included:
ratio_fieldsupport for per-event ratio sampling ((0, 1]).rate_fieldsupport for per-event1/Nsampling (positive integer).rateorratio) as a default value (dynamic-only mode is not allowed).ratio_fieldandrate_fieldtogether (mirrors existingratevsratioexclusivity).sample_rate_keypopulated with the effective sampling value used for each sampled event.Vector configuration
How did you test this PR?
Ran locally:
cargo fmt --allcargo test --no-default-features --features transforms-sample sample:: --libcargo clippy --no-default-features --features transforms-sample --lib --tests -- -D warnings./scripts/check_changelog_fragments.shTest coverage added/updated includes:
Change Type
Is this a breaking change?
Does this PR include user facing changes?
no-changeloglabel to this PR.References
sampletransform