diff --git a/CHANGELOG.md b/CHANGELOG.md index ed236fb2a3..0c96e60003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ## 25.1.0 +**Features**: + +- Use a separate rate-limit enforcement category for replay-video envelope items. ([#4459](https://github.com/getsentry/relay/pull/4459)) + **Internal**: - Updates performance score calculation on spans and events to also store cdf values as measurements. ([#4438](https://github.com/getsentry/relay/pull/4438)) diff --git a/relay-server/src/envelope.rs b/relay-server/src/envelope.rs index e29444199c..8a099628a6 100644 --- a/relay-server/src/envelope.rs +++ b/relay-server/src/envelope.rs @@ -700,9 +700,10 @@ impl Item { ItemType::UserReport => smallvec![], ItemType::UserReportV2 => smallvec![(DataCategory::UserReportV2, 1)], ItemType::Profile => smallvec![(DataCategory::Profile, 1)], - ItemType::ReplayEvent | ItemType::ReplayRecording | ItemType::ReplayVideo => { + ItemType::ReplayEvent | ItemType::ReplayRecording => { smallvec![(DataCategory::Replay, 1)] } + ItemType::ReplayVideo => smallvec![(DataCategory::ReplayVideo, 1)], ItemType::ClientReport => smallvec![], ItemType::CheckIn => smallvec![(DataCategory::Monitor, 1)], ItemType::Span | ItemType::OtelSpan => smallvec![(DataCategory::Span, 1)], diff --git a/relay-server/src/utils/rate_limits.rs b/relay-server/src/utils/rate_limits.rs index c2d1ac9854..7eb91c1ca8 100644 --- a/relay-server/src/utils/rate_limits.rs +++ b/relay-server/src/utils/rate_limits.rs @@ -161,6 +161,9 @@ pub struct EnvelopeSummary { /// The number of replays. pub replay_quantity: usize, + /// The number of replay videos. + pub replay_video_quantity: usize, + /// The number of monitor check-ins. pub monitor_quantity: usize, @@ -236,7 +239,7 @@ impl EnvelopeSummary { DataCategory::Session => &mut self.session_quantity, DataCategory::Profile => &mut self.profile_quantity, DataCategory::Replay => &mut self.replay_quantity, - DataCategory::ReplayVideo => &mut self.replay_quantity, + DataCategory::ReplayVideo => &mut self.replay_video_quantity, DataCategory::Monitor => &mut self.monitor_quantity, DataCategory::Span => &mut self.span_quantity, DataCategory::ProfileChunk => &mut self.profile_chunk_quantity, @@ -342,6 +345,8 @@ pub struct Enforcement { pub profiles_indexed: CategoryLimit, /// The combined replay item rate limit. pub replays: CategoryLimit, + /// The combined replay video item rate limit. + pub replay_videos: CategoryLimit, /// The combined check-in item rate limit. pub check_ins: CategoryLimit, /// The combined spans rate limit. @@ -384,6 +389,7 @@ impl Enforcement { profiles, profiles_indexed, replays, + replay_videos, check_ins, spans, spans_indexed, @@ -399,6 +405,7 @@ impl Enforcement { profiles, profiles_indexed, replays, + replay_videos, check_ins, spans, spans_indexed, @@ -485,7 +492,7 @@ impl Enforcement { ItemType::Session => !self.sessions.is_active(), ItemType::Profile => !self.profiles_indexed.is_active(), ItemType::ReplayEvent => !self.replays.is_active(), - ItemType::ReplayVideo => !self.replays.is_active(), + ItemType::ReplayVideo => !self.replay_videos.is_active(), ItemType::ReplayRecording => !self.replays.is_active(), ItemType::CheckIn => !self.check_ins.is_active(), ItemType::Span | ItemType::OtelSpan | ItemType::OtelTracesData => { @@ -757,6 +764,21 @@ where rate_limits.merge(replay_limits); } + // Handle replay video. + // Remove: 2025-04-06 + if summary.replay_video_quantity > 0 { + let item_scoping = scoping.item(DataCategory::ReplayVideo); + let replay_limits = self + .check + .apply(item_scoping, summary.replay_video_quantity)?; + enforcement.replay_videos = CategoryLimit::new( + DataCategory::ReplayVideo, + summary.replay_video_quantity, + replay_limits.longest(), + ); + rate_limits.merge(replay_limits); + } + // Handle monitor checkins. if summary.monitor_quantity > 0 { let item_scoping = scoping.item(DataCategory::Monitor); @@ -1270,16 +1292,34 @@ mod tests { /// Limit replays. #[test] fn test_enforce_limit_replays() { - let mut envelope = envelope![ReplayEvent, ReplayRecording, ReplayVideo]; + let mut envelope = envelope![ReplayEvent, ReplayRecording]; let mut mock = MockLimiter::default().deny(DataCategory::Replay); let (enforcement, limits) = enforce_and_apply(&mut mock, &mut envelope, None); assert!(limits.is_limited()); assert_eq!(envelope.envelope().len(), 0); - mock.assert_call(DataCategory::Replay, 3); + mock.assert_call(DataCategory::Replay, 2); + + assert_eq!(get_outcomes(enforcement), vec![(DataCategory::Replay, 2),]); + } + + /// Limit replays. + #[test] + fn test_enforce_limit_replay_video() { + let mut envelope = envelope![ReplayVideo]; + + let mut mock = MockLimiter::default().deny(DataCategory::ReplayVideo); + let (enforcement, limits) = enforce_and_apply(&mut mock, &mut envelope, None); + + assert!(limits.is_limited()); + assert_eq!(envelope.envelope().len(), 0); + mock.assert_call(DataCategory::ReplayVideo, 1); - assert_eq!(get_outcomes(enforcement), vec![(DataCategory::Replay, 3),]); + assert_eq!( + get_outcomes(enforcement), + vec![(DataCategory::ReplayVideo, 1),] + ); } /// Limit monitor checkins.