Skip to content

Support async/fire-and-forget enforcement off the critical path #14366

Description

@davidxia

kgateway version

2.3.2

Is your feature request related to a problem? Please describe.

I want global rate limiting but don't want every incoming request to result in a call to the rate limiting service which will add latency to each request. I want asynchronous rate limiting (sometimes called async-evaluate or post-evaluate) where the proxy lets the request through while simultaneously asking the rate limit service for a decision. If the limit is hit, only the next request is blocked.

I read this is how companies like Cloudflare implement their rate limiting system.

This is why the increment jobs are run asynchronously without slowing down the requests. If the request rate is above the threshold, another piece of data is stored asking all servers in the PoP to start applying the mitigation for that client. Only this bit of information is checked during request processing.

Kgateway's global rate limiting doc suggests the only mode is synchronous.

  1. The Gateway extracts descriptors and sends them to the Rate Limit Service.
  2. The Rate Limit Service applies configured limits for those descriptors and returns a decision to the Gateway.
  3. If allowed, the Gateway forwards the request to the App.
  4. If the rate limit is reached, the Gateway denies the request and returns a message to the Client.

Describe the solution you'd like

Envoy's global HTTP rate-limit filter supports apply_on_stream_done on route/vhost-level
RateLimit actions (envoy.config.route.v3.RateLimit.apply_on_stream_done), which sends the
rate-limit-service call after the stream completes instead of gating the request:

A rate limit applied on the stream completion is "fire-and-forget" by nature, and rate limit
is not enforced by this config. In other words, the current request won't be blocked when this
is true, but the budget will be updated for the subsequent requests based on the action with
this field set to true.

This removes the RLS round trip from the request's critical path — the current request proceeds
immediately, and only a later request can be denied once the budget is exceeded.

kgateway's TrafficPolicy.spec.rateLimit.global (RateLimitPolicy in
api/v1alpha1/kgateway/traffic_policy_types.go) currently only exposes descriptors and
extensionRef, with no way to set this field. Requesting it be surfaced on
RateLimitDescriptor/RateLimitPolicy so users can trade strict enforcement for latency
(e.g. combining a synchronous descriptor for a hard ceiling with an apply_on_stream_done
descriptor for cost/usage-based accounting).

Describe alternatives you've considered

No response

Additional Context

Related: this is distinct from #6540 (shadow mode), which never enforces at all rather than
deferring enforcement to a subsequent request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    No fields configured for Feature.

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions