fix(limit-req): Make Redis path atomic via EVAL + use hash key with TTL#12605
fix(limit-req): Make Redis path atomic via EVAL + use hash key with TTL#12605falvaradorodriguez wants to merge 15 commits intoapache:masterfrom
Conversation
25d03d5 to
3530220
Compare
- Switch Redis storage to a single hash key (cluster compatibility) - Perform read/compute/write atomically with EVAL - Keep first-hit behavior (no cost on missing state) - Add EX-based TTL to avoid key buildup
3530220 to
d60f099
Compare
|
Hi @falvaradorodriguez, we need to add the test case for this fix. |
88ed813 to
74d8587
Compare
|
Hi @falvaradorodriguez, there are failed CI that need fixing. |
40a7f03 to
af4d670
Compare
af4d670 to
6399925
Compare
Hi @Baoyuantop! The failures are not related to the current changes. Tests of the modified plugin seems to be fine. Please, Can you review? Thanks |
|
When will this PR be merged? I am having the same issue that this PR fixes 🙏 |
|
I will promptly urge other community maintainers to review. |
shreemaan-abhishek
left a comment
There was a problem hiding this comment.
please resolve the conflicts too.
There was a problem hiding this comment.
Pull request overview
This PR fixes race conditions in the limit-req Redis/Redis-Cluster policies by moving the limiter state into a single Redis hash key and updating it atomically via a single Lua EVAL script (with EVALSHA fast-path for standalone Redis), aligning behavior with the intended rate-limit semantics under concurrency.
Changes:
- Replaces multi-key
GET/SETupdates with an atomic Redis Lua script operating on one hash key (excess+last) plus TTL. - Enables
EVALSHAoptimization for the standalone Redis policy; usesEVALfor Redis Cluster for reliability. - Adds test cases for Redis and Redis Cluster to validate hash structure usage, TTL presence, and basic limiting behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
apisix/plugins/limit-req/util.lua |
Implements atomic Redis script (hash-based state + TTL) with EVALSHA fallback logic. |
apisix/plugins/limit-req/limit-req-redis.lua |
Enables use_evalsha for standalone Redis limiter instances. |
apisix/plugins/limit-req/limit-req-redis-cluster.lua |
Disables use_evalsha for cluster limiter instances (uses EVAL). |
t/plugin/limit-req-redis.t |
Adds tests for hash-key state + TTL and a rapid-request rejection case for Redis policy. |
t/plugin/limit-req-redis-cluster.t |
Adds analogous tests for Redis Cluster policy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
moonming
left a comment
There was a problem hiding this comment.
Hi @falvaradorodriguez, thank you for making the Redis limit-req path atomic via EVAL!
Using a Lua script with EVAL to ensure atomicity of the rate limiting operation is the correct approach — the current non-atomic multi-command flow can have race conditions under high concurrency. With 12 reviews, this has been thoroughly discussed.
To confirm readiness:
- Are all 12 review comments addressed?
- Has the EVAL script been tested under concurrent load to verify it resolves the race condition?
- The hash key with TTL approach — does it handle key expiration correctly for sliding windows?
This is an important correctness fix. Let's get it finalized! Thank you.
|
Hi @falvaradorodriguez, I see no problems with the code here. Could you please fix the failing test? |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Hi @moonming. I’m replying on your comment:
Yes, they have all been checked and corrected where necessary.
Yes, in fact, in my case it has been validated in an environment with a high volume of requests
Yes, also validated.
Thank you for the review! |
Hi @Baoyuantop! As you can see here, the tests that were failing were not related to this PR.
Could you check that again? Thanks |
|
Okay, I'll check it after CI finishes executing. |
|
It seems the failed CI is unrelated to this PR; I'll try to fix it. |


Description
The current
limit-reqRedis implementation uses two separate keys (excess and last) and updates them with multiple GET/SET operations.Solution
Store both values (excess and last) under a single Redis hash key, so the state is managed as one unit.
Use a single EVAL script that performs read → compute → write atomically inside Redis, removing race conditions. This approach is consistent with how the
limit-countplugin already works.Add a TTL to the key to avoid buildup of stale state.
Preserve existing semantics: the first request with no prior state does not consume tokens.
Which issue(s) this PR fixes:
Fixes #12592
Checklist