Serving admission-plane open-source landscape¶
Research date: 2026-09-21
Scope: Candidate open-source components for the admission lifecycle proposed in GitHub issue 22.
Status: Build-versus-adopt input; primary-source review, not a production qualification.
Executive answer¶
No reviewed project satisfies the complete contract as-is. Do not build the service from a blank
page, however: Kuadrant Limitador is the strongest adaptable foundation. Its current main-line
Kuadrant API adds an expiring Reserve and actual-usage Commit; its storage contract requires
all matching counters to be admitted or none. That is materially closer than ordinary gateway rate
limiters.
Limitador still needs a compatibility layer or upstream changes before it can be the canonical
admission plane. The current reservation has one scalar amount, Reserve has no caller idempotency
key, Commit always applies actual usage and is therefore not retry-idempotent, and there is no
renew/progress operation, explicit cancellation operation, weighted concurrency type, or
declarative overload-action response. Its own README says the API is not stable, and the
reservation work is visible in testing pre-releases rather than a settled API
(Limitador README,
Limitador releases).
The recommended decision is therefore adopt and extend Limitador after a focused spike, while keeping an owned, transport-neutral admission API in front of it. Fall back to implementing the small state engine behind that API only if the spike disproves Redis atomicity/latency, cell-local partitioning, or upstreamability. Envoy remains the preferred gateway integration; neither Envoy nor APISIX should define the domain contract.
Required contract¶
The comparison treats the following as required: trusted multidimensional descriptors; rate and burst plus weighted concurrency; atomic all-or-none acquisition across every matched limit; expiring, retry-idempotent permits; estimated multi-resource work followed by actual-usage settlement; cancellation; renewal/progress for streams; request idempotency; an allowlisted declarative overload/fallback action; distributed cell-local state; and a gateway seam.
Candidate assessment¶
| Candidate | What is available | Decisive gaps | Classification |
|---|---|---|---|
| Kuadrant Limitador | A service or Rust library with conditional, variable-qualified rate counters and memory or Redis storage. Its Kuadrant gRPC API now exposes Reserve(domain, descriptors, amount, ttl) and Commit(..., reservation_id, actual_amount). The storage interface specifies all-or-none reservation across matching counters, and reservation entries expire (service protocol, reservation model, storage contract, server/storage options). |
One scalar amount is applied uniformly, so one call cannot reserve different CPU/GPU/token quantities. The server creates the reservation ID rather than accepting an idempotency key. Commit documents unconditional actual-usage application, so replay can double-charge. Expiry exists, but renew/progress and cancellation do not; Commit(0) would only be an implicit release convention. There is no typed concurrency policy or overload action. |
Adaptable foundation; closest candidate. |
Envoy Global Rate Limit Service plus envoyproxy/ratelimit |
The standard protocol accepts arbitrary hierarchical descriptors, multiple descriptors, and a weighted hits_addend; it denies the request when any descriptor is over limit. The reference service has Redis-backed rate counters, nested configuration, shadow mode, and negative-hit refunds (RLS protocol, reference service). |
The protocol response has codes, per-descriptor status, headers/body, and metadata, but no permit, TTL, idempotency key, settle, cancel, or renew operation. The reference implementation pipelines per-key increments; its optional STOP_CACHE_KEY_INCREMENT_WHEN_OVERLIMIT only prevents some increments after observing an over-limit key and is not an atomic all-or-none reservation (Redis implementation). It models rate windows, not held concurrency. |
Adopt as gateway transport/rate-limit component, not as the admission engine. |
| Envoy external processing, overload manager, and RLQS | External processing can inspect a stream and immediately reject or modify it. The overload manager locally sheds work in response to proxy resource pressure. RLQS distributes expiring rate-quota assignments to Envoy instances and receives bucket usage reports (external processing, overload manager, rate-limit quota). | These are integration and local protection mechanisms. None defines request-scoped capacity permits, atomic mixed rate/concurrency admission, settlement, cancellation, or renewal. RLQS assignment TTL belongs to a quota bucket, not to admitted work. | Complement, not a substitute. |
| Apache APISIX 3.18 | limit-req supplies local or Redis-backed rate/burst limits; limit-conn supplies local or Redis-backed concurrent-request limits and decrements them in the log phase. ai-rate-limiting dry-runs quota at access time, then accounts provider-reported token usage in the log phase; it accepts token expressions and Redis-backed counters (rate limiting, concurrency limiting, AI token limiting). |
Multiple rules are executed sequentially, and rate, concurrency, and AI accounting are separate plugins. The source does not roll back earlier acquisitions when a later rule/plugin rejects, so the combination is not one atomic admission (limit-count rule loop, limit-conn lifecycle). AI usage is charged after completion rather than reserved from a multidimensional estimate. There is no external idempotent permit, renewal/progress, cancellation, or overload-action contract. | Strong gateway replacement only if the lifecycle is deliberately simplified; otherwise non-fit as the canonical plane. |
Netflix concurrency-limits |
A Java library provides adaptive in-process concurrency limits, immediate rejection, gRPC/Servlet integration, and request partitions (project README). | It has no distributed counter state, rate/burst policy, atomic hierarchy, durable/expiring permit protocol, usage settlement, or gateway-independent service API. | Useful backend-local safety layer; non-fit for the shared plane. |
| Resilience4j Bulkhead/RateLimiter | A maintained Java fault-tolerance library provides per-service semaphore/thread-pool bulkheads, rate limiters, completion release, and application-level fallback composition (project documentation, bulkhead API). | State and lifecycle are application-process concerns. It does not provide distributed multidimensional admission, an atomic mixed-limit transaction, leases, settlement, or a fleet gateway protocol. | Useful application-local defense; non-fit for the shared plane. |
| Open Policy Agent | OPA is a mature declarative policy decision point with Envoy integration, structured input/output, bundles, and decision logs. It could select an allowlisted action from trusted request context (OPA deployment model, OPA-Envoy plugin). | OPA explicitly says it is not the source of truth for external dynamic data. Counters, reservations, atomic mutation, expiry, settlement, and renewal would still need another service; pulling them during evaluation adds that service to every decision path (external data). | Optional policy evaluator, not the admission-state engine. |
Kong was not carried into the detailed shortlist: its relevant open-source surface overlaps the ordinary rate-limit/gateway category already represented by Envoy, Limitador, and APISIX, without evidence of the required permit-and-settlement lifecycle.
Fit against the hard requirements¶
| Requirement | Closest existing support | Remaining work |
|---|---|---|
| Trusted multidimensional descriptors | Envoy RLS and Limitador descriptors; APISIX gateway variables | Bind only authenticated/routing-derived fields and enforce cardinality bounds. |
| Rate/burst | Envoy RLS/Limitador window counters; APISIX rate/burst plugins | Preserve one atomic transaction with concurrency and reservations. |
| Weighted concurrency | APISIX counts concurrent requests; Limitador reservations hold a scalar amount | Add a typed weighted-concurrency rule whose capacity returns on release/expiry rather than a rate-window counter. |
| Atomic all-or-none matched limits | Limitador's reservation storage contract | Prove the selected production backend and Redis topology with race/failure tests; add heterogeneous resource amounts. |
| Expiring permit | Limitador reservation TTL | Add a stable permit envelope, policy version, and client request identity. |
| Idempotent admit/settle/cancel | None | Add an idempotency ledger and conflict semantics; make duplicate terminal operations return the first result without charging again. |
| Estimate then actual settlement | Limitador Reserve/Commit; APISIX post-response AI usage accounting |
Make settlement retry-safe and accept a bounded resource vector. |
| Stream progress/renewal | Envoy external processing can observe a stream, but exposes no capacity renewal | Add explicit renew/progress with monotonic sequence and maximum lifetime. |
| Declarative overload action | OPA can return structured policy output; gateways can reject or route | Define an allowlisted action identifier in the admission decision; execution stays in the gateway/orchestrator. |
| Distributed/cell-local state | Limitador and APISIX support Redis; Envoy RLS uses Redis | Enforce cell/pool colocation and avoid cross-cell hot keys. |
| Gateway integration | Envoy RLS/ext-proc and APISIX | Prefer an Envoy adapter to the owned transport-neutral API; do not make the gateway product the domain interface. |
Recommended spike and stop conditions¶
Use Limitador's reservation implementation as the first spike, not as an immediate production
dependency. The spike should prove: (1) atomic reserve across every matched rule under concurrency;
(2) cell-local Redis key colocation and bounded state expiry; (3) a replay-safe wrapper for
Admit, Settle, Cancel, and Renew; (4) heterogeneous resource vectors without independent
partial acquisition; and (5) gateway deadline/failure behavior under Redis and admission-service
faults.
Proceed with adoption when those semantics can be added as a small upstreamable extension or thin owned service layer. Implement the state engine behind the same API when doing so would require a deep fork, when Redis topology prevents atomic multi-key acquisition, or when measured admission latency/availability misses the agreed budget. In either case, reuse Envoy integration and keep backend-local adaptive concurrency as a second safety layer rather than conflating it with global fairness and accounting.