feat: limit Redis server connections - #3498
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a server-side admission limit for Redis-only listeners to prevent idle clients (and, for SSL, expensive TLS handshakes) from consuming connection resources before request-level concurrency limits apply.
Changes:
- Introduces
ServerOptions.redis_max_connectionsandServer::SetRedisMaxConnections()to configure/update the Redis connection admission limit (default unlimited). - Enforces a “dedicated Redis listener” contract and reserves connection slots in
Acceptorimmediately afteraccept()(beforeSocket::Create()/ TLS). - Adds stats (
ServerStatistics.rejected_redis_connection_count), documentation updates (EN/CN), and unit tests covering rejection behavior and dynamic updates.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/brpc_server_unittest.cpp | Adds regression tests for dedicated-listener validation, plaintext rejection, TLS pre-handshake rejection, and dynamic limit updates. |
| src/brpc/server.h | Adds redis_max_connections, rejected_redis_connection_count, and Server::SetRedisMaxConnections() API surface. |
| src/brpc/server.cpp | Validates dedicated Redis-only configuration, wires redis limit into acceptor startup, aggregates rejection stats, implements runtime setter. |
| src/brpc/acceptor.h | Extends StartAccept signature and adds atomics/methods for connection slot accounting and rejection stats. |
| src/brpc/acceptor.cpp | Implements slot reservation before socket creation, relaxed-atomic accounting, and plaintext/TLS rejection behavior. |
| docs/en/server.md | Documents Redis connection limiting, dedicated listener requirements, runtime setter, and stats. |
| docs/cn/server.md | Chinese documentation for the Redis connection limiting feature and behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…-connections # Conflicts: # src/brpc/server.cpp
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Accept fd 0 in the Redis connection tests and describe every dedicated-listener requirement in startup and runtime errors. Validation: brpc_server_unittest rebuilt; dedicated-listener, plaintext/dynamic-limit, pre-TLS rejection, and idle-connection tests passed (4/4). Diff whitespace check passed. Full suite not run.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Document that rejection borrows the fd from the accept loop guard, which closes it on continue. Describe nonblocking best-effort error delivery in both server guides. Accumulate short TCP reads in rejection tests and verify plaintext EOF. Validation: rebuilt brpc_server_unittest; dedicated-listener, plaintext/dynamic-limit, pre-TLS rejection and idle-connection tests passed (4/4). Merge-base diff whitespace check passed. Full suite not run. Production behavior is unchanged.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Guard MSG_NOSIGNAL and use SO_NOSIGPIPE when available; omit the optional error if per-socket signal suppression is unavailable. Share listener validation diagnostics, use RAII based on actual Server ownership in the invalid-configuration test, and verify new admission after slot recovery. Validation: cmake --build build --target brpc_server_unittest --parallel 4 succeeded. Dedicated-listener, plaintext/dynamic-limit/slot-recovery, pre-TLS rejection, and idle-connection tests passed (4/4). Merge-base diff check passed. Full suite and macOS fallback were not run.
There was a problem hiding this comment.
🔵 Needs a closer look
It changes core connection-acceptance behavior (including SSL-path early rejection) and public API/ABI surface, warranting final human review despite targeted tests and documentation.
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
- Review effort level: Lite
What problem does this PR solve?
Issue Number: N/A
Problem Summary:
RedisServicehas no server-side connection limit.max_concurrencylimits requests only after protocol parsing, so idle clients can consume all available connection resources. On an SSL listener, accepting excess clients into brpc also allows them to enter the comparatively expensive TLS handshake before an application can reject them.What is changed and the side effects?
Changed:
ServerOptions.redis_max_connections(0keeps the existing unlimited behavior).Server::SetRedisMaxConnections()to atomically change the limit on a running Redis-only Server. Raising or disabling the limit affects subsequent accepts; lowering it does not close established connections.Acceptor, before creating a brpcSocket, so idle clients count and concurrent accepts cannot exceed the limit.redis_serviceset,enabled_protocols="redis", builtin services disabled, and no RPC or other protocol services on thatServer. A dedicated listener can start unlimited and enable the limit later.-ERR max number of clients reachedto excess plaintext clients, then close the connection. Backpressure or socket errors may prevent some or all of the response from being delivered. If SSL is configured, close the accepted fd immediately without creating a brpcSocketor starting TLS.MSG_NOSIGNALand fall back toSO_NOSIGPIPEwhere available. If per-socket SIGPIPE suppression is unavailable or fails, omit the optional plaintext error and close the connection.Serverinstances unlimited, and expose the cumulative rejection count asServerStatistics.rejected_redis_connection_count.Side effects:
ServerOptionsandServerStatistics, so applications using a prebuilt shared brpc library must rebuild with the updated headers and library.Check List:
Tests / Checks:
macOS /
SO_NOSIGPIPEfallback not built or run locally. Linux validation below exercisesMSG_NOSIGNAL.cmake --build build --target brpc_server_unittest --parallel 4Focused tests pass: dedicated-listener validation, runtime enable/raise/lower behavior, RPC isolation, plaintext rejection with short reads and EOF, pre-TLS rejection, and
ServerTest.close_idle_connections(4/4 tests; rerun on 2026-09-10 after the portability and slot-recovery follow-up).The complete
brpc_server_unittestwas not rerun in this follow-up. The previously recorded full-suite run was not clean in this local environment: the existing timing-sensitive overload assertions inServerTest.http_error_codeandServerTest.max_concurrencyobserve a successful third RPC instead of overload rejection. The focused tests above pass; this PR does not change request concurrency code.git diff --check upstream/master...HEADCode / Documentation:
Reviewer focus:
accept, release onSocket::Createfailure orBeforeRecycle, and update the limit with a relaxed store.Rollback:
Call
SetRedisMaxConnections(0)(or start withredis_max_connections=0) to retain unlimited admission, or revert these commits to remove the API and accounting fields.