@@ -20,26 +20,26 @@ pub struct Scoping {
20
20
}
21
21
22
22
/// Accumulator of all cardinality limiter rejections.
23
- pub trait Rejections {
23
+ pub trait Rejections < ' a > {
24
24
/// Called for ever [`Entry`] which was rejected from the [`Limiter`].
25
- fn reject ( & mut self , entry_id : EntryId ) ;
25
+ fn reject ( & mut self , limit_id : & ' a str , entry_id : EntryId ) ;
26
26
}
27
27
28
28
/// Limiter responsible to enforce limits.
29
29
pub trait Limiter {
30
30
/// Verifies cardinality limits.
31
31
///
32
32
/// Returns an iterator containing only accepted entries.
33
- fn check_cardinality_limits < E , R > (
33
+ fn check_cardinality_limits < ' a , E , R > (
34
34
& self ,
35
35
scoping : Scoping ,
36
- limits : & [ CardinalityLimit ] ,
36
+ limits : & ' a [ CardinalityLimit ] ,
37
37
entries : E ,
38
38
rejections : & mut R ,
39
39
) -> Result < ( ) >
40
40
where
41
41
E : IntoIterator < Item = Entry > ,
42
- R : Rejections ;
42
+ R : Rejections < ' a > ;
43
43
}
44
44
45
45
/// Unit of operation for the cardinality limiter.
@@ -100,30 +100,30 @@ impl<T: Limiter> CardinalityLimiter<T> {
100
100
/// Checks cardinality limits of a list of buckets.
101
101
///
102
102
/// Returns an iterator of all buckets that have been accepted.
103
- pub fn check_cardinality_limits < I : CardinalityItem > (
103
+ pub fn check_cardinality_limits < ' a , I : CardinalityItem > (
104
104
& self ,
105
105
scoping : Scoping ,
106
- limits : & [ CardinalityLimit ] ,
106
+ limits : & ' a [ CardinalityLimit ] ,
107
107
items : Vec < I > ,
108
- ) -> Result < CardinalityLimits < I > , ( Vec < I > , Error ) > {
108
+ ) -> Result < CardinalityLimits < ' a , I > , ( Vec < I > , Error ) > {
109
109
metric ! ( timer( CardinalityLimiterTimers :: CardinalityLimiter ) , {
110
110
let entries = items. iter( ) . enumerate( ) . filter_map( |( id, item) | {
111
111
Some ( Entry :: new( EntryId ( id) , item. namespace( ) ?, item. to_hash( ) ) )
112
112
} ) ;
113
113
114
- let mut rejections = RejectedIds :: default ( ) ;
114
+ let mut rejections = RejectionTracker :: default ( ) ;
115
115
if let Err ( err) =
116
116
self . limiter
117
117
. check_cardinality_limits( scoping, limits, entries, & mut rejections)
118
118
{
119
119
return Err ( ( items, err) ) ;
120
120
}
121
121
122
- if !rejections. 0 . is_empty( ) {
122
+ if !rejections. entries . is_empty( ) {
123
123
relay_log:: debug!(
124
124
scoping = ?scoping,
125
125
"rejected {} metrics due to cardinality limit" ,
126
- rejections. 0 . len( ) ,
126
+ rejections. entries . len( ) ,
127
127
) ;
128
128
}
129
129
@@ -136,30 +136,46 @@ impl<T: Limiter> CardinalityLimiter<T> {
136
136
///
137
137
/// The result can be used directly by [`CardinalityLimits`].
138
138
#[ derive( Debug , Default ) ]
139
- struct RejectedIds ( HashSet < usize > ) ;
139
+ struct RejectionTracker < ' a > {
140
+ limits : HashSet < & ' a str > ,
141
+ entries : HashSet < usize > ,
142
+ }
140
143
141
- impl Rejections for RejectedIds {
144
+ impl < ' a > Rejections < ' a > for RejectionTracker < ' a > {
142
145
#[ inline( always) ]
143
- fn reject ( & mut self , entry_id : EntryId ) {
144
- self . 0 . insert ( entry_id. 0 ) ;
146
+ fn reject ( & mut self , limit_id : & ' a str , entry_id : EntryId ) {
147
+ self . limits . insert ( limit_id) ;
148
+ self . entries . insert ( entry_id. 0 ) ;
145
149
}
146
150
}
147
151
148
152
/// Result of [`CardinalityLimiter::check_cardinality_limits`].
149
153
#[ derive( Debug ) ]
150
- pub struct CardinalityLimits < T > {
154
+ pub struct CardinalityLimits < ' a , T > {
151
155
source : Vec < T > ,
152
156
rejections : HashSet < usize > ,
157
+ limits : HashSet < & ' a str > ,
153
158
}
154
159
155
- impl < T > CardinalityLimits < T > {
156
- fn new ( source : Vec < T > , rejections : RejectedIds ) -> Self {
160
+ impl < ' a , T > CardinalityLimits < ' a , T > {
161
+ fn new ( source : Vec < T > , rejections : RejectionTracker < ' a > ) -> Self {
157
162
Self {
158
163
source,
159
- rejections : rejections. 0 ,
164
+ rejections : rejections. entries ,
165
+ limits : rejections. limits ,
160
166
}
161
167
}
162
168
169
+ /// Returns `true` if any items have been rejected.
170
+ pub fn has_rejections ( & self ) -> bool {
171
+ !self . rejections . is_empty ( )
172
+ }
173
+
174
+ /// Returns all id's of cardinality limits which were exceeded.
175
+ pub fn enforced_limits ( & self ) -> & HashSet < & ' a str > {
176
+ & self . limits
177
+ }
178
+
163
179
/// Recovers the original list of items passed to the cardinality limiter.
164
180
pub fn into_source ( self ) -> Vec < T > {
165
181
self . source
@@ -259,21 +275,27 @@ mod tests {
259
275
let limits = CardinalityLimits {
260
276
source : vec ! [ 'a' , 'b' , 'c' , 'd' , 'e' ] ,
261
277
rejections : HashSet :: from ( [ 0 , 1 , 3 ] ) ,
278
+ limits : HashSet :: new ( ) ,
262
279
} ;
263
280
assert_rejected ( & limits, [ 'a' , 'b' , 'd' ] ) ;
281
+ assert ! ( limits. has_rejections( ) ) ;
264
282
assert_eq ! ( limits. into_accepted( ) , vec![ 'c' , 'e' ] ) ;
265
283
266
284
let limits = CardinalityLimits {
267
285
source : vec ! [ 'a' , 'b' , 'c' , 'd' , 'e' ] ,
268
286
rejections : HashSet :: from ( [ ] ) ,
287
+ limits : HashSet :: new ( ) ,
269
288
} ;
270
289
assert_rejected ( & limits, [ ] ) ;
290
+ assert ! ( !limits. has_rejections( ) ) ;
271
291
assert_eq ! ( limits. into_accepted( ) , vec![ 'a' , 'b' , 'c' , 'd' , 'e' ] ) ;
272
292
273
293
let limits = CardinalityLimits {
274
294
source : vec ! [ 'a' , 'b' , 'c' , 'd' , 'e' ] ,
275
295
rejections : HashSet :: from ( [ 0 , 1 , 2 , 3 , 4 ] ) ,
296
+ limits : HashSet :: new ( ) ,
276
297
} ;
298
+ assert ! ( limits. has_rejections( ) ) ;
277
299
assert_rejected ( & limits, [ 'a' , 'b' , 'c' , 'd' , 'e' ] ) ;
278
300
assert ! ( limits. into_accepted( ) . is_empty( ) ) ;
279
301
}
@@ -283,19 +305,19 @@ mod tests {
283
305
struct RejectAllLimiter ;
284
306
285
307
impl Limiter for RejectAllLimiter {
286
- fn check_cardinality_limits < I , T > (
308
+ fn check_cardinality_limits < ' a , I , T > (
287
309
& self ,
288
310
_scoping : Scoping ,
289
- _limits : & [ CardinalityLimit ] ,
311
+ _limits : & ' a [ CardinalityLimit ] ,
290
312
entries : I ,
291
313
outcomes : & mut T ,
292
314
) -> Result < ( ) >
293
315
where
294
316
I : IntoIterator < Item = Entry > ,
295
- T : Rejections ,
317
+ T : Rejections < ' a > ,
296
318
{
297
319
for entry in entries {
298
- outcomes. reject ( entry. id ) ;
320
+ outcomes. reject ( "test" , entry. id ) ;
299
321
}
300
322
301
323
Ok ( ( ) )
@@ -304,17 +326,19 @@ mod tests {
304
326
305
327
let limiter = CardinalityLimiter :: new ( RejectAllLimiter ) ;
306
328
329
+ let limits = build_limits ( ) ;
307
330
let result = limiter
308
331
. check_cardinality_limits (
309
332
build_scoping ( ) ,
310
- & build_limits ( ) ,
333
+ & limits ,
311
334
vec ! [
312
335
Item :: new( 0 , MetricNamespace :: Transactions ) ,
313
336
Item :: new( 1 , MetricNamespace :: Transactions ) ,
314
337
] ,
315
338
)
316
339
. unwrap ( ) ;
317
340
341
+ assert_eq ! ( result. enforced_limits( ) , & HashSet :: from( [ "test" ] ) ) ;
318
342
assert ! ( result. into_accepted( ) . is_empty( ) ) ;
319
343
}
320
344
@@ -323,16 +347,16 @@ mod tests {
323
347
struct AcceptAllLimiter ;
324
348
325
349
impl Limiter for AcceptAllLimiter {
326
- fn check_cardinality_limits < I , T > (
350
+ fn check_cardinality_limits < ' a , I , T > (
327
351
& self ,
328
352
_scoping : Scoping ,
329
- _limits : & [ CardinalityLimit ] ,
353
+ _limits : & ' a [ CardinalityLimit ] ,
330
354
_entries : I ,
331
355
_outcomes : & mut T ,
332
356
) -> Result < ( ) >
333
357
where
334
358
I : IntoIterator < Item = Entry > ,
335
- T : Rejections ,
359
+ T : Rejections < ' a > ,
336
360
{
337
361
Ok ( ( ) )
338
362
}
@@ -344,8 +368,9 @@ mod tests {
344
368
Item :: new( 0 , MetricNamespace :: Transactions ) ,
345
369
Item :: new( 1 , MetricNamespace :: Spans ) ,
346
370
] ;
371
+ let limits = build_limits ( ) ;
347
372
let result = limiter
348
- . check_cardinality_limits ( build_scoping ( ) , & build_limits ( ) , items. clone ( ) )
373
+ . check_cardinality_limits ( build_scoping ( ) , & limits , items. clone ( ) )
349
374
. unwrap ( ) ;
350
375
351
376
assert_eq ! ( result. into_accepted( ) , items) ;
@@ -356,7 +381,7 @@ mod tests {
356
381
struct RejectEvenLimiter ;
357
382
358
383
impl Limiter for RejectEvenLimiter {
359
- fn check_cardinality_limits < I , T > (
384
+ fn check_cardinality_limits < ' a , I , T > (
360
385
& self ,
361
386
scoping : Scoping ,
362
387
limits : & [ CardinalityLimit ] ,
@@ -365,14 +390,14 @@ mod tests {
365
390
) -> Result < ( ) >
366
391
where
367
392
I : IntoIterator < Item = Entry > ,
368
- T : Rejections ,
393
+ T : Rejections < ' a > ,
369
394
{
370
395
assert_eq ! ( scoping, build_scoping( ) ) ;
371
396
assert_eq ! ( limits, & build_limits( ) ) ;
372
397
373
398
for entry in entries {
374
399
if entry. id . 0 % 2 == 0 {
375
- outcomes. reject ( entry. id ) ;
400
+ outcomes. reject ( "test" , entry. id ) ;
376
401
}
377
402
}
378
403
0 commit comments