Skip to content

Commit 3e73284

Browse files
committed
Merge branch 'master' into feat/performance
2 parents 2fa1db3 + 04515ba commit 3e73284

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

sentry-types/src/protocol/envelope.rs

+34
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,40 @@ impl Envelope {
140140
.next()
141141
}
142142

143+
/// Filters the Envelope's [`EnvelopeItem`]s based on a predicate,
144+
/// and returns a new Envelope containing only the filtered items.
145+
///
146+
/// Retains the [`EnvelopeItem`]s for which the predicate returns `true`.
147+
/// Additionally, [`EnvelopeItem::Attachment`]s are only kept if the Envelope
148+
/// contains an [`EnvelopeItem::Event`] or [`EnvelopeItem::Transaction`].
149+
///
150+
/// [`None`] is returned if no items remain in the Envelope after filtering.
151+
pub fn filter<P>(self, mut predicate: P) -> Option<Self>
152+
where
153+
P: FnMut(&EnvelopeItem) -> bool,
154+
{
155+
let mut filtered = Envelope::new();
156+
for item in self.items {
157+
if predicate(&item) {
158+
filtered.add_item(item);
159+
}
160+
}
161+
162+
// filter again, removing attachments which do not make any sense without
163+
// an event/transaction
164+
if filtered.uuid().is_none() {
165+
filtered
166+
.items
167+
.retain(|item| !matches!(item, EnvelopeItem::Attachment(..)))
168+
}
169+
170+
if filtered.items.is_empty() {
171+
None
172+
} else {
173+
Some(filtered)
174+
}
175+
}
176+
143177
/// Serialize the Envelope into the given [`Write`].
144178
///
145179
/// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html

sentry/src/transports/ratelimit.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use httpdate::parse_http_date;
22
use std::time::{Duration, SystemTime};
33

4-
// TODO: maybe move this someplace where we can filter an `Envelope`s items.
4+
use crate::protocol::EnvelopeItem;
5+
use crate::Envelope;
56

67
/// A Utility that helps with rate limiting sentry requests.
78
#[derive(Debug, Default)]
@@ -10,6 +11,7 @@ pub struct RateLimiter {
1011
error: Option<SystemTime>,
1112
session: Option<SystemTime>,
1213
transaction: Option<SystemTime>,
14+
attachment: Option<SystemTime>,
1315
}
1416

1517
impl RateLimiter {
@@ -55,6 +57,7 @@ impl RateLimiter {
5557
"error" => self.error = new_time,
5658
"session" => self.session = new_time,
5759
"transaction" => self.transaction = new_time,
60+
"attachment" => self.attachment = new_time,
5861
_ => {}
5962
}
6063
}
@@ -66,7 +69,10 @@ impl RateLimiter {
6669
}
6770
}
6871

69-
/// Query the RateLimiter for a certain category of event.
72+
/// Query the RateLimiter if a certain category of event is currently rate limited.
73+
///
74+
/// If the given category is rate limited, it will return the remaining
75+
/// [`Duration`] for which it is.
7076
pub fn is_disabled(&self, category: RateLimitingCategory) -> Option<Duration> {
7177
if let Some(ts) = self.global {
7278
let time_left = ts.duration_since(SystemTime::now()).ok();
@@ -79,14 +85,38 @@ impl RateLimiter {
7985
RateLimitingCategory::Error => self.error,
8086
RateLimitingCategory::Session => self.session,
8187
RateLimitingCategory::Transaction => self.transaction,
88+
RateLimitingCategory::Attachment => self.attachment,
8289
}?;
8390
time_left.duration_since(SystemTime::now()).ok()
8491
}
92+
93+
/// Query the RateLimiter for a certain category of event.
94+
///
95+
/// Returns `true` if the category is *not* rate limited and should be sent.
96+
pub fn is_enabled(&self, category: RateLimitingCategory) -> bool {
97+
self.is_disabled(category).is_none()
98+
}
99+
100+
/// Filters the [`Envelope`] according to the current rate limits.
101+
///
102+
/// Returns [`None`] if all the envelope items were filtered out.
103+
pub fn filter_envelope(&self, envelope: Envelope) -> Option<Envelope> {
104+
envelope.filter(|item| {
105+
self.is_enabled(match item {
106+
EnvelopeItem::Event(_) => RateLimitingCategory::Error,
107+
EnvelopeItem::SessionUpdate(_) | EnvelopeItem::SessionAggregates(_) => {
108+
RateLimitingCategory::Session
109+
}
110+
EnvelopeItem::Transaction(_) => RateLimitingCategory::Transaction,
111+
EnvelopeItem::Attachment(_) => RateLimitingCategory::Attachment,
112+
_ => RateLimitingCategory::Any,
113+
})
114+
})
115+
}
85116
}
86117

87118
/// The Category of payload that a Rate Limit refers to.
88119
#[non_exhaustive]
89-
#[allow(dead_code)]
90120
pub enum RateLimitingCategory {
91121
/// Rate Limit for any kind of payload.
92122
Any,
@@ -96,6 +126,8 @@ pub enum RateLimitingCategory {
96126
Session,
97127
/// Rate Limit pertaining to Transactions.
98128
Transaction,
129+
/// Rate Limit pertaining to Attachments.
130+
Attachment,
99131
}
100132

101133
#[cfg(test)]

sentry/src/transports/thread.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ impl TransportThread {
5858
};
5959

6060
if let Some(time_left) = rl.is_disabled(RateLimitingCategory::Any) {
61-
sentry_debug!(
62-
"Skipping event send because we're disabled due to rate limits for {}s",
63-
time_left.as_secs()
64-
);
65-
continue;
66-
}
67-
rl = send(envelope, rl).await;
61+
sentry_debug!(
62+
"Skipping event send because we're disabled due to rate limits for {}s",
63+
time_left.as_secs()
64+
);
65+
continue;
66+
}
67+
match rl.filter_envelope(envelope) {
68+
Some(envelope) => {
69+
rl = send(envelope, rl).await;
70+
},
71+
None => {
72+
sentry_debug!("Envelope was discarded due to per-item rate limits");
73+
},
74+
};
6875
}
6976
})
7077
})

0 commit comments

Comments
 (0)