@@ -3,6 +3,7 @@ use std::str::FromStr;
3
3
use std:: sync:: { Arc , Mutex , PoisonError } ;
4
4
use std:: time:: { Duration , Instant } ;
5
5
6
+ use relay_base_schema:: data_category:: DataCategory ;
6
7
use relay_base_schema:: metrics:: MetricNamespace ;
7
8
use relay_base_schema:: organization:: OrganizationId ;
8
9
use relay_base_schema:: project:: { ProjectId , ProjectKey } ;
@@ -482,8 +483,31 @@ impl CachedRateLimits {
482
483
483
484
let mut inner = self . 0 . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
484
485
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) ;
487
511
}
488
512
}
489
513
@@ -499,6 +523,23 @@ impl CachedRateLimits {
499
523
}
500
524
}
501
525
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
+
502
543
#[ cfg( test) ]
503
544
mod tests {
504
545
use smallvec:: smallvec;
0 commit comments