Skip to content

Commit a0db3c5

Browse files
committed
ref(quotas): Enforce span limits for transactions and vice versa
1 parent ecfafa8 commit a0db3c5

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

relay-quotas/src/rate_limit.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::str::FromStr;
33
use std::sync::{Arc, Mutex, PoisonError};
44
use std::time::{Duration, Instant};
55

6+
use relay_base_schema::data_category::DataCategory;
67
use relay_base_schema::metrics::MetricNamespace;
78
use relay_base_schema::organization::OrganizationId;
89
use relay_base_schema::project::{ProjectId, ProjectKey};
@@ -482,8 +483,31 @@ impl CachedRateLimits {
482483

483484
let mut inner = self.0.lock().unwrap_or_else(PoisonError::into_inner);
484485
let current = Arc::make_mut(&mut inner);
485-
for limit in limits {
486-
current.add(limit)
486+
for mut limit in limits {
487+
// To spice it up, we do have some special casing here for 'inherited categories',
488+
// e.g. spans and transactions.
489+
//
490+
// The tldr; is, as transactions are just containers for spans,
491+
// we can enforce span limits on transactions but also vice versa.
492+
//
493+
// So this is largely an enforcement problem, but since Relay propagates
494+
// rate limits to clients, we clone the limits with the alternate category,
495+
// to have old SDKs rate limit correctly, but also to simplify client
496+
// implementations. Only Relay needs to make this decision.
497+
for i in 0..limit.categories.len() {
498+
let Some(category) = limit.categories.get(i) else {
499+
debug_assert!(false, "logical error");
500+
break;
501+
};
502+
503+
for inherited in inherited_categories(category) {
504+
if !limit.categories.contains(inherited) {
505+
limit.categories.push(*inherited);
506+
}
507+
}
508+
}
509+
510+
current.add(limit);
487511
}
488512
}
489513

@@ -499,6 +523,23 @@ impl CachedRateLimits {
499523
}
500524
}
501525

526+
/// Returns inherited rate limit categories for the passed category.
527+
///
528+
/// When a rate limit for a category can also be enforced in a different category,
529+
/// then it's an inherited category.
530+
///
531+
/// For example, a transaction rate limit can also be applied to spans and vice versa.
532+
///
533+
/// For a detailed explanation on span/transaction enforcement see:
534+
/// <https://develop.sentry.dev/ingestion/relay/transaction-span-ratelimits/>.
535+
fn inherited_categories(category: &DataCategory) -> &'static [DataCategory] {
536+
match category {
537+
DataCategory::Transaction => &[DataCategory::Span],
538+
DataCategory::Span => &[DataCategory::Transaction],
539+
_ => &[],
540+
}
541+
}
542+
502543
#[cfg(test)]
503544
mod tests {
504545
use smallvec::smallvec;

0 commit comments

Comments
 (0)