Repository navigation
Add concurrent request rate limiter and optimize middleware stack - #5361
Open
serdarozerr wants to merge 14 commits into
Open
serdarozerr wants to merge 14 commits into
serdarozerr wants to merge 14 commits into
Conversation
… done. Introduces ConcurrencyRateLimiter middleware to limit concurrent requests per user across all endpoints. Supports separate logging and blocking thresholds for smooth rollout, Redis and in-memory store backends, thread-safe singleton limiter instance, and RetryAfter header estimation based on request duration. Disabled by default via config. Also separates user DB lookup from token decoding into a dedicated UserContextSetter middleware, and moves RequestMetrics after rate limiters in the middleware stack.
…cs and RequestLogs preserved
2 of 3 tasks
philippthun
reviewed
Sep 11, 2026
stephanme
added a commit
that referenced
this pull request
Sep 15, 2026
- implementation on #5361 was improved and reaches more throughput now - all measurements run for 300s - more detailed explanation how to determine max throughput - some more arguments for replacing nginx by a dedicated CAPI router
- Introduce ConcurrentRequestCounter with InMemoryStore and RedisStore backends using atomic Lua scripts for check-and-increment; rejected requests do not increment the counter (no phantom count) - Add ConcurrencyRateLimiter and ServiceBrokerRateLimiter middlewares backed by the new counter - Extract UserId, InternalOrRootApi, TooManyRequests, BasicAuth mixins - Derive redis_connection_pool_size and redis_counter_ttl_seconds from puma max_threads and request_timeout_in_seconds in config - Add unit tests for all new components
philippthun
force-pushed
the
feature/concurrent-ratelimiter
branch
from
October 14, 2026 15:28
1f6a60f to
842ca76
Compare
Extract a shared BaseConcurrencyRateLimiter that both ConcurrencyRateLimiter and ServiceBrokerRateLimiter inherit, removing the duplicated call/increment/decrement logic. Pull the admin? check into a reusable Admin mixin. Simplify RackAppBuilder#build by moving each limiter's option hash into its own helper method, dropping the rubocop length disables. Drop the dead redis_connection_pool_size/redis_counter_ttl_seconds kwargs. Each RedisStore now takes only the socket and resolves its pool size from puma max_threads (and TTL from request_timeout_in_seconds) itself, applied to the concurrent, expiring, and statsd counters. Update specs accordingly, including coverage for the set and unset pool-size paths, and add admin mixin specs.
philippthun
force-pushed
the
feature/concurrent-ratelimiter
branch
from
October 14, 2026 15:47
842ca76 to
bce3d81
Compare
Emit the rejecting limiter's class name on every 429 so nginx can log which rate limiter blocked the request.
philippthun
force-pushed
the
feature/concurrent-ratelimiter
branch
from
October 15, 2026 11:34
eb01c7c to
cb4c8c3
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Concurrent request rate limiter: Introduced ConcurrencyRateLimiter middleware that limits the number of concurrent in-flight requests per user (or IP for unauthenticated requests). Supports separate logging_limit (warn only) and blocking_limit (return 429) for smooth rollout. if both are active, blocking_limit takes precedence and logging_limit is ignored. Disabled by default via config. Backed by Redis (with TTL-based safety cleanup) or in-memory store for single-process deployments. Counter increments use an atomic Lua script to guarantee no phantom counts. So rejected requests never increment the counter. Admins are rate limited the same as regular users (unlike the service broker rate limiter, which exempts them).
Middleware stack optimization: Split
SecurityContextSetterinto two concerns — token decoding only (configure_token_only) happens early in the stack, and user DB lookup (configure_user) is deferred to a newUserContextSettermiddleware placed after rate limiters. This means rate limiting never triggers a DB lookup for blocked requests.RequestMetricsis also moved after rate limiters so that blocked 429 responses are excluded from request metrics, keeping metrics focused on requests that actually reached the application.Changes
middleware/concurrency_rate_limiter.rb— newConcurrencyRateLimitermiddlewaremiddleware/service_broker_rate_limiter.rb— refactored onto the shared base classmiddleware/base_concurrency_rate_limiter.rb— shared base for the two concurrency limitersmiddleware/concurrent_request_counter.rb—ConcurrentRequestCounterwithRedisStore/InMemoryStoremiddleware/mixins/admin.rb— extractedadmin?helpermiddleware/user_context_setter.rb— new middleware for deferred user DB lookupmiddleware/security_context_setter.rb— now callsconfigure_token_onlyinstead of fullconfigurelib/cloud_controller/security/security_context_configurer.rb— addedconfigure_token_onlyandconfigure_usermethodslib/cloud_controller/rack_app_builder.rb— wired new middlewares, movedRequestMetricsafter rate limiterslib/cloud_controller/config_schemas/api_schema.rb— addedconcurrency_rate_limiterconfig schemaRetry-Afterheader added on 429 responses from the concurrent rate limiter.Related PRs
Concurrency ratelimiter specs capi-release#679
I have reviewed the contributing guide
I have viewed, signed, and submitted the Contributor License Agreement
I have made this pull request to the
mainbranchI have run all the unit tests using
bundle exec rakeI have run CF Acceptance Tests