1
- import type { TransportMakeRequestResponse } from '@sentry/types' ;
1
+ import type { DataCategory , TransportMakeRequestResponse } from '@sentry/types' ;
2
2
3
3
// Intentionally keeping the key broad, as we don't know for sure what rate limit headers get returned from backend
4
4
export type RateLimits = Record < string , number > ;
@@ -32,15 +32,15 @@ export function parseRetryAfterHeader(header: string, now: number = Date.now()):
32
32
*
33
33
* @return the time in ms that the category is disabled until or 0 if there's no active rate limit.
34
34
*/
35
- export function disabledUntil ( limits : RateLimits , category : string ) : number {
36
- return limits [ category ] || limits . all || 0 ;
35
+ export function disabledUntil ( limits : RateLimits , dataCategory : DataCategory ) : number {
36
+ return limits [ dataCategory ] || limits . all || 0 ;
37
37
}
38
38
39
39
/**
40
40
* Checks if a category is rate limited
41
41
*/
42
- export function isRateLimited ( limits : RateLimits , category : string , now : number = Date . now ( ) ) : boolean {
43
- return disabledUntil ( limits , category ) > now ;
42
+ export function isRateLimited ( limits : RateLimits , dataCategory : DataCategory , now : number = Date . now ( ) ) : boolean {
43
+ return disabledUntil ( limits , dataCategory ) > now ;
44
44
}
45
45
46
46
/**
@@ -67,23 +67,32 @@ export function updateRateLimits(
67
67
* rate limit headers are of the form
68
68
* <header>,<header>,..
69
69
* where each <header> is of the form
70
- * <retry_after>: <categories>: <scope>: <reason_code>
70
+ * <retry_after>: <categories>: <scope>: <reason_code>: <namespaces>
71
71
* where
72
72
* <retry_after> is a delay in seconds
73
73
* <categories> is the event type(s) (error, transaction, etc) being rate limited and is of the form
74
74
* <category>;<category>;...
75
75
* <scope> is what's being limited (org, project, or key) - ignored by SDK
76
76
* <reason_code> is an arbitrary string like "org_quota" - ignored by SDK
77
+ * <namespaces> Semicolon-separated list of metric namespace identifiers. Defines which namespace(s) will be affected.
78
+ * Only present if rate limit applies to the metric_bucket data category.
77
79
*/
78
80
for ( const limit of rateLimitHeader . trim ( ) . split ( ',' ) ) {
79
- const [ retryAfter , categories ] = limit . split ( ':' , 2 ) ;
81
+ const [ retryAfter , categories , , , namespaces ] = limit . split ( ':' , 5 ) ;
80
82
const headerDelay = parseInt ( retryAfter , 10 ) ;
81
83
const delay = ( ! isNaN ( headerDelay ) ? headerDelay : 60 ) * 1000 ; // 60sec default
82
84
if ( ! categories ) {
83
85
updatedRateLimits . all = now + delay ;
84
86
} else {
85
87
for ( const category of categories . split ( ';' ) ) {
86
- updatedRateLimits [ category ] = now + delay ;
88
+ if ( category === 'metric_bucket' ) {
89
+ // namespaces will be present when category === 'metric_bucket'
90
+ if ( ! namespaces || namespaces . split ( ';' ) . includes ( 'custom' ) ) {
91
+ updatedRateLimits [ category ] = now + delay ;
92
+ }
93
+ } else {
94
+ updatedRateLimits [ category ] = now + delay ;
95
+ }
87
96
}
88
97
}
89
98
}
0 commit comments