Skip to content

Commit f077e38

Browse files
authored
feat(metrics): Tag the sample decision on count_per_root_project (#1870)
Adds a binary `decision` tag to `c:transactions/count_per_root_project`. This allows us to query the effective sample rate on projects. Background: Dynamic sampling is based on a target sample rate, called the organization's _"fidelity"_. On top of that, Sentry computes a set of _priorities_ (formerly called "biases") to increase visibility in under-represented areas. For example, Sentry boosts releases during adoption or small projects. The priorities currently apply on top of the fidelity rate and increase the sample rate for a subset of transactions. Overall, this increases the effective sample rate applied for a project. The extent of this is not deterministic, as it depends on the match rate of sample rules. The final goal is to adjust the priorities and base sample rate in such a way that the effective sample rate matches the target fidelity.
1 parent 7cb05e0 commit f077e38

File tree

6 files changed

+149
-41
lines changed

6 files changed

+149
-41
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
**Features**:
66

77
- Protocol validation for source map image type. ([#1869](https://github.com/getsentry/relay/pull/1869))
8+
- Add PHP support for profiling. ([#1871](https://github.com/getsentry/relay/pull/1871))
89

910
**Internal**:
1011

1112
- Revert back the addition of metric names as tag on Sentry errors when relay drops metrics. ([#1873](https://github.com/getsentry/relay/pull/1873))
12-
- Add PHP support. ([#1871](https://github.com/getsentry/relay/pull/1871))
13+
- Tag the dynamic sampling decision on `count_per_root_project` to measure effective sample rates. ([#1870](https://github.com/getsentry/relay/pull/1870))
1314

1415
## 23.2.0
1516

relay-dynamic-config/src/utils.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ mod tests {
6060
if expected_error.is_empty() {
6161
assert!(res.is_ok(), "{:?}", (input, res.unwrap_err()));
6262
} else {
63-
assert!(res.is_err(), "{}", input);
64-
assert_eq!(res.unwrap_err().to_string(), expected_error, "{}", input);
63+
assert!(res.is_err(), "{input}");
64+
assert_eq!(res.unwrap_err().to_string(), expected_error, "{input}");
6565
}
6666
}
6767
}

relay-server/src/actors/processor.rs

+31-10
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,13 @@ struct ProcessEnvelopeState {
283283
/// resulting item.
284284
sample_rates: Option<Value>,
285285

286+
/// The result of a dynamic sampling operation on this envelope.
287+
///
288+
/// This defaults to [`SamplingResult::Keep`] and is determined based on dynamic sampling rules
289+
/// in the project configuration. In the drop case, this contains a list of rules that applied
290+
/// on the envelope.
291+
sampling_result: SamplingResult,
292+
286293
/// Metrics extracted from items in the envelope.
287294
///
288295
/// Relay can extract metrics for sessions and transactions, which is controlled by
@@ -1298,6 +1305,7 @@ impl EnvelopeProcessorService {
12981305
transaction_metrics_extracted: false,
12991306
metrics: Metrics::default(),
13001307
sample_rates: None,
1308+
sampling_result: SamplingResult::Keep,
13011309
extracted_metrics: Default::default(),
13021310
project_state,
13031311
sampling_project_state,
@@ -2000,9 +2008,10 @@ impl EnvelopeProcessorService {
20002008
extraction_config,
20012009
&project_config.metric_conditional_tagging,
20022010
event,
2011+
transaction_from_dsc,
2012+
&state.sampling_result,
20032013
&mut state.extracted_metrics.project_metrics,
20042014
&mut state.extracted_metrics.sampling_metrics,
2005-
transaction_from_dsc,
20062015
);
20072016
}
20082017
);
@@ -2118,17 +2127,20 @@ impl EnvelopeProcessorService {
21182127
}
21192128

21202129
/// Run dynamic sampling rules to see if we keep the envelope or remove it.
2121-
fn sample_envelope(&self, state: &mut ProcessEnvelopeState) -> Result<(), ProcessingError> {
2122-
let client_ip = state.envelope.meta().client_addr();
2123-
2124-
match utils::should_keep_event(
2130+
fn compute_sampling_decision(&self, state: &mut ProcessEnvelopeState) {
2131+
state.sampling_result = utils::should_keep_event(
21252132
self.config.processing_enabled(),
21262133
&state.project_state,
21272134
state.sampling_project_state.as_deref(),
21282135
state.envelope.dsc(),
21292136
state.event.value(),
2130-
client_ip,
2131-
) {
2137+
state.envelope.meta().client_addr(),
2138+
);
2139+
}
2140+
2141+
/// Apply the dynamic sampling decision from `compute_sampling_decision`.
2142+
fn sample_envelope(&self, state: &mut ProcessEnvelopeState) -> Result<(), ProcessingError> {
2143+
match std::mem::take(&mut state.sampling_result) {
21322144
SamplingResult::Drop(rule_ids) => {
21332145
state
21342146
.envelope_context
@@ -2210,6 +2222,7 @@ impl EnvelopeProcessorService {
22102222
self.light_normalize_event(state)?;
22112223
self.normalize_dsc(state);
22122224
self.filter_event(state)?;
2225+
self.compute_sampling_decision(state);
22132226
self.extract_transaction_metrics(state)?;
22142227
self.sample_envelope(state)?;
22152228

@@ -2692,7 +2705,10 @@ mod tests {
26922705
..Event::default()
26932706
};
26942707

2695-
for (sample_rate, shouldkeep) in [(0.0, false), (1.0, true)] {
2708+
for (sample_rate, expected_result) in [
2709+
(0.0, SamplingResult::Drop(MatchedRuleIds(vec![RuleId(1)]))),
2710+
(1.0, SamplingResult::Keep),
2711+
] {
26962712
let project_state = state_with_rule_and_condition(
26972713
Some(sample_rate),
26982714
RuleType::Transaction,
@@ -2706,6 +2722,7 @@ mod tests {
27062722
transaction_metrics_extracted: false,
27072723
metrics: Default::default(),
27082724
sample_rates: None,
2725+
sampling_result: SamplingResult::Keep,
27092726
extracted_metrics: Default::default(),
27102727
project_state: Arc::new(project_state),
27112728
sampling_project_state: None,
@@ -2715,8 +2732,12 @@ mod tests {
27152732
TestSemaphore::new(42).try_acquire().unwrap(),
27162733
),
27172734
};
2718-
let result = service.sample_envelope(&mut state);
2719-
assert_eq!(result.is_ok(), shouldkeep);
2735+
2736+
// TODO: This does not test if the sampling decision is actually applied. This should be
2737+
// refactored to send a proper Envelope in and call process_state to cover the full
2738+
// pipeline.
2739+
service.compute_sampling_decision(&mut state);
2740+
assert_eq!(state.sampling_result, expected_result);
27202741
}
27212742
}
27222743

0 commit comments

Comments
 (0)