Add token bucket rate limiting for burst control - #37
Conversation
Background: Cinder API Exhaustion InvestigationThis PR addresses a production issue identified during investigation of Cinder API exhaustion in our OpenStack regions. Root CauseGardener's Kubernetes CSI driver issues volume create requests in parallel (4+ concurrent requests per project). Each
Why the Sliding Window Doesn't HelpThe existing sliding window algorithm ( How the Token Bucket Solves ThisWith Deployment Plan
|
Adds an optional token bucket algorithm alongside the existing sliding window. The token bucket limits request bursts while preserving the same sustained rate, preventing database lock contention caused by bursty clients (e.g., Gardener CSI driver volume creates). Configuration: append ',burst=N' to a rate limit string: limit: 100r/m,burst=3 This allows at most 3 concurrent requests while maintaining 100r/m sustained throughput. Requests beyond the burst capacity are delayed or rejected with 429. New files: - rate_limit/lua/redis_token_bucket.lua (algorithm) - rate_limit/tests/test_token_bucket.py (unit + integration tests) - TOKEN_BUCKET_SPEC.md (design spec) Modified files: - rate_limit/units.py (parse_token_bucket_rate_limit) - rate_limit/backend.py (load script, route on burst=, new method) - README.md (document both algorithms) - docs/configure.md (token bucket configuration guide)
6d0ee6f to
87fea11
Compare
joker-at-work
left a comment
There was a problem hiding this comment.
This article states "The database is paused while a script runs." - did you investigate if we lose parallelism and/or performance because of that?
|
@joker-at-work Good catch — the article's claim is technically correct: Redis Lua scripts block the server during execution (Redis docs confirm this: "While executing the script, all server activities are blocked during its entire runtime"). However, this PR doesn't introduce that property — the existing sliding window algorithm ( I ran a benchmark on Redis 7 to compare both scripts directly: Single-call latency (1000 iterations, fresh keys)
Throughput
Why the token bucket is actually slightly fasterThe token bucket script issues fewer Redis commands per execution:
Practical impactThe p50 of 223µs means Redis can handle ~4,500 token bucket evaluations per second on a single core, sequential. Cinder's API traffic is on the order of tens to low-hundreds of req/sec per region — three orders of magnitude below where script-blocking would become a bottleneck. If we ever did approach that limit, the mitigation is well-known: shard the Redis instance per service (Cinder has its own Redis sub-chart) or move to Redis Cluster — both are independent of the algorithm choice. Update: Detailed analysis of the linked articleI read through the full article. The "database is paused" line is presented rhetorically — the author defers concrete performance numbers to Part 2. The only quantitative ceiling claim is in their forward reference:
100K req/sec/counter is ~1000× our actual Cinder API load. The article makes several other claims worth examining against this PR:
Notably, the author's own conclusion supports adopting a token bucket over a sliding window:
So the article's own logic is: if you must use Redis, use a token bucket via Lua — which is exactly what this PR enables. Beyond the burst-control benefit for Cinder, this PR also fixes the "memory exhaustion on rejected requests" defect the article identifies in the existing sliding window implementation. |
Summary
Adds an optional token bucket algorithm alongside the existing sliding window for controlling request bursts. This prevents database lock contention caused by bursty clients (e.g., Gardener CSI driver firing multiple volume creates in parallel).
Configuration
Append
,burst=Nto any existing rate limit string:This allows at most 3 concurrent requests while maintaining 100 requests/minute sustained throughput. Requests beyond the burst capacity are delayed or rejected with 429.
Changes
rate_limit/lua/redis_token_bucket.luarate_limit/units.pyparse_token_bucket_rate_limit()parserrate_limit/backend.pyburst=; new__rate_limit_token_bucket()rate_limit/tests/test_token_bucket.pyTOKEN_BUCKET_SPEC.mdREADME.mddocs/configure.mdBackward Compatibility
Xr/Ytconfigs (noburst=) continue using the sliding window unchangedTesting
All 14 new tests pass (8 unit + 6 Redis integration). Existing test suite passes with no regressions.
Context
See
TOKEN_BUCKET_SPEC.mdfor full design rationale and the Cinder API exhaustion investigation.