RpcRateLimitInterceptor class
Rate-limiting interceptor for RpcResponderEndpoint.
RpcRateLimitInterceptor(
global: RpcRateLimit.slidingWindow(max: 5000, window: Duration(seconds: 1)),
perService: {'Heavy': RpcRateLimit.slidingWindow(max: 10, window: second)},
perMethod: {'Sync.push': RpcRateLimit.tokenBucket(max: 10, window: second)},
perKeyFallback: RpcRateLimit.slidingWindow(max: 50, window: second),
keyExtractor: (call) => call.context.getValue(userIdKey),
)
Which counters a call charges
global is a CEILING, not a fallback. It applies to every call, and one
more specific limit applies alongside it: perMethod[key] >
perService[key] > perKeyFallback[key:method], the first that matches and
only that one. BOTH must admit, so the tighter of the two binds and a call
refused by either costs nothing in the other.
So global: 5/s with perMethod: {'Feed.hot': 1000/s} admits 5 calls to
Feed.hot per second, not 1000. Set global above every specific limit, or
leave it unset, if that is not what you want.
Without keyExtractor every slot is ONE shared counter. With it, each key
gets independent perService and perMethod counters, so one caller cannot
exhaust another's budget; global stays shared either way. A key of null
skips the dynamic limits and charges global alone.
perKeyFallback is a catch-all per (key, method) for calls no other spec
matches — the same thing as listing every method, without enumerating them.
What a streaming call costs
Every shape is admitted at establishment, before the handler runs, so no call is free. Metering then follows the direction of client-driven load:
- Unary — one token per call.
- Client-stream / bidirectional — one token per INBOUND request message, MINIMUM ONE: the establishment token covers the first message. So a ten-message upload costs ten as it always did, and a call that sends no request messages costs one instead of nothing. That last case is not hypothetical — a bidi subscription is exactly it, zero requests with the responses pushed by the server, and per-message metering alone let an unlimited number of them through. This cannot be disabled.
- Server-stream — one token at establishment, like a unary call.
Responses are server-paced output rather than client load, and charging
them lets a burst of pushes tear down a long-lived subscription. Set
meterServerStreamMessagesto charge per emitted response instead, e.g. to cap a client-attributable firehose; the stream then errors RESOURCE_EXHAUSTED and stops when the limit is reached.
Enforced within a single Dart isolate, so no locking. Idle counters are swept as calls arrive; dispose drops them all at shutdown.
- Inheritance
-
- Object
- IRpcInterceptor
- RpcRateLimitInterceptor
Constructors
-
RpcRateLimitInterceptor({RpcRateLimit? global, Map<
String, RpcRateLimit> perService = const {}, Map<String, RpcRateLimit> perMethod = const {}, RpcRateLimit? perKeyFallback, String? keyExtractor(RpcCallInfo)?, Duration cleanupInterval = const Duration(minutes: 5), int maxTrackedKeys = 100000, bool meterServerStreamMessages = false, DateTime clock()?}) - Creates a rate limiter.
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
Methods
-
check(
RpcCallInfo call) → FutureOr< void> -
Runs before this interceptor's method for unary, server-stream,
client-stream and bidirectional calls alike.
inherited
-
dispose(
) → void - Releases dynamic counter state and stops enforcing.
-
interceptBidirectionalStream<
TRequest, TResponse> (RpcCallInfo call, Stream< TRequest> requests, RpcBidirectionalStreamNext<TRequest, TResponse> next) → FutureOr<Stream< TResponse> > -
Bidirectional-stream call interceptor.
override
-
interceptClientStream<
TRequest, TResponse> (RpcCallInfo call, Stream< TRequest> requests, RpcClientStreamNext<TRequest, TResponse> next) → Future<TResponse> -
Client-stream call interceptor.
override
-
interceptServerStream<
TRequest, TResponse> (RpcCallInfo call, TRequest request, RpcServerStreamNext< TRequest, TResponse> next) → FutureOr<Stream< TResponse> > -
Server-stream call interceptor.
override
-
interceptUnary<
TRequest, TResponse> (RpcCallInfo call, TRequest request, RpcUnaryNext< TRequest, TResponse> next) → Future<TResponse> -
Unary call interceptor.
override
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited