Description
Token-window admission checks in both backends admit a request while the committed count is exactly at the limit, and reject only when it is strictly over:
- memory:
FixedWindowCounter::is_exceeded (crates/aisix-ratelimit/src/window.rs) — if self.count > limit
- Redis: the acquire script's token loop (
crates/aisix-ratelimit/src/store/redis.rs) — if cur > limit
Token limiting is post-paid (check at admission, commit actual usage after the response), so crossing the cap by one in-flight response is inherent. But the strict > adds an avoidable case: a caller whose committed usage lands on the cap exactly (count == limit, budget fully consumed) still gets one more request admitted, overshooting by up to a full response beyond the crossing that post-paid semantics already implies.
This applies uniformly to every token window (tpm, tpd; policy-derived and key-level alike) — it is not specific to any one window and predates #771/#949, which only added a new window over the same counters.
Proposal
Flip both comparisons to >= ("budget fully consumed ⇒ stop admitting") in the same change, with an exact-limit regression case per backend — e.g. cap 10, upstream reports exactly 10, the next request must be 429. Request counters are pre-paid (would_be > limit with the increment included) and are already exact; they need no change.
Both backends must move together (memory ↔ redis swap must not change observable limits).
Priority
Low — one-request overshoot at an exact boundary; found during review of #949.
Description
Token-window admission checks in both backends admit a request while the committed count is exactly at the limit, and reject only when it is strictly over:
FixedWindowCounter::is_exceeded(crates/aisix-ratelimit/src/window.rs) —if self.count > limitcrates/aisix-ratelimit/src/store/redis.rs) —if cur > limitToken limiting is post-paid (check at admission, commit actual usage after the response), so crossing the cap by one in-flight response is inherent. But the strict
>adds an avoidable case: a caller whose committed usage lands on the cap exactly (count == limit, budget fully consumed) still gets one more request admitted, overshooting by up to a full response beyond the crossing that post-paid semantics already implies.This applies uniformly to every token window (
tpm,tpd; policy-derived and key-level alike) — it is not specific to any one window and predates #771/#949, which only added a new window over the same counters.Proposal
Flip both comparisons to
>=("budget fully consumed ⇒ stop admitting") in the same change, with an exact-limit regression case per backend — e.g. cap 10, upstream reports exactly 10, the next request must be 429. Request counters are pre-paid (would_be > limitwith the increment included) and are already exact; they need no change.Both backends must move together (
memory ↔ redisswap must not change observable limits).Priority
Low — one-request overshoot at an exact boundary; found during review of #949.