Add rate-limit module: Bucket4j 8.21 token buckets on Redis in a Spring Boot 4.1 filter, per-user/per-key/global limits, 429 with Retry-After, two instances sharing one bucket
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b7rnC7jppGGdHGESvy
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
== alice sends 12 requests with no pause
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=9 reset=6 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=8 reset=12 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=7 reset=18 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=6 reset=24 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=5 reset=30 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=4 reset=36 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=3 reset=42 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=2 reset=48 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=1 reset=54 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=0 reset=60 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=60 retry-after=6 scope=user
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=60 retry-after=6 scope=user
|
||||
-- body of the 429:
|
||||
{"type":"about:blank","title":"Too Many Requests","status":429,"detail":"Rate limit exceeded for scope 'user'. Retry in 6 second(s).","retryAfterSeconds":6}
|
||||
== alice is out of tokens; bob is a different bucket
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=60 retry-after=6 scope=user
|
||||
GET /api/hello X-User-Id: bob -> 200 limit=10 remaining=9 reset=6 retry-after=null scope=user
|
||||
== alice waits exactly Retry-After (6 s on the test clock) and tries again
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=60 retry-after=6 scope=user
|
||||
-- after 5 s: still too early
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=55 retry-after=1 scope=user
|
||||
-- after 6 s in total:
|
||||
GET /api/hello X-User-Id: alice -> 200 limit=10 remaining=0 reset=60 retry-after=null scope=user
|
||||
GET /api/hello X-User-Id: alice -> 429 limit=10 remaining=0 reset=60 retry-after=6 scope=user
|
||||
== /health is outside /api/, so the filter skips it
|
||||
30 requests to /health -> all 200, X-RateLimit-Limit header present: false
|
||||
Reference in New Issue
Block a user