Skip to content

Source rate-limit limit value from dynamic metadata #9212

Description

@kanurag94

Description:

A BackendTrafficPolicy global rate-limit rule currently carries a static limit (RateLimitValue{Requests, Unit}). Add the ability to source the limit per-request from dynamic metadata, the same way cost is already sourced from metadata via RateLimitCostFromMetadata (HitsAddend).

Use case: a multi-tenant gateway where each tenant has a different limit but all share one BackendTrafficPolicy. An upstream ext_proc writes the tenant limit into dynamic metadata and the rule reads it per-request, instead of creating a policy/route per tenant.

Envoy supports this on the route-level rate-limit action via RateLimit.limit (RateLimit_Override with a DynamicMetadata source). It uses the Global rate-limit service (RLS) path, the same path cost-from-metadata uses.

Proposed change, symmetric with cost-from-metadata:

API (api/v1alpha1/ratelimit_types.go) — add an optional metadata source to RateLimitValue, analogous to RateLimitCostMetadata. Requests/Unit stay required and act as the default when the metadata is absent. Restrict to Global via CEL.

type RateLimitValue struct {
    Requests uint32        `json:"requests"`
    Unit     RateLimitUnit `json:"unit"`
    // FromMetadata, when set, overrides Requests/Unit per-request from dynamic
    // metadata. The metadata value must be a struct with `requests_per_unit`
    // and `unit`. Requests/Unit are used as the default when metadata is absent.
    // Only supported for Global rate limits.
    // +optional
    FromMetadata *RateLimitValueMetadata `json:"fromMetadata,omitempty"`
}

type RateLimitValueMetadata struct {
    // +kubebuilder:validation:Required
    Namespace string `json:"namespace"`
    // +kubebuilder:validation:Required
    Key string `json:"key"`
}

IR (internal/ir/xds.go) — carry the metadata source on ir.RateLimitRule (regenerate deepcopy).

Translator (internal/xds/translator/ratelimit.go, buildRouteRateLimits) — set the override on the route-level action, mirroring the existing HitsAddend block:

if m := rule.LimitMetadata; m != nil {
    rateLimit.Limit = &routev3.RateLimit_Override{
        OverrideSpecifier: &routev3.RateLimit_Override_DynamicMetadata_{
            DynamicMetadata: &routev3.RateLimit_Override_DynamicMetadata{
                MetadataKey: &metadatav3.MetadataKey{
                    Key: m.Namespace,
                    Path: []*metadatav3.MetadataKey_PathSegment{{
                        Segment: &metadatav3.MetadataKey_PathSegment_Key{Key: m.Key},
                    }},
                },
            },
        },
    }
}

Note: the override expects the metadata value to be a struct {requests_per_unit, unit}, not a plain number like cost. The ext_proc must write that struct.

Relevant Links:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions