fix(channel): Prevent leaked SocketMap references during reinitializa… - #3433
fix(channel): Prevent leaked SocketMap references during reinitializa…#3433darion-yaphet wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes brpc::Channel reinitialization for direct (single-server) channels by ensuring the exact SocketMapKey used during initialization is preserved and properly released, preventing leaked SocketMap references and avoiding partial state overwrites when reinitialization fails.
Changes:
- Track and remove the precise
SocketMapKeyfor the active single-server channel state to prevent staleSocketMapreferences after reinit/destruction. - Rework initialization to build
ChannelOptions/protocol function pointers locally and only commit to theChannelon success, keeping prior state intact on failure. - Add unit tests covering repeated direct initialization and failed reinitialization behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| test/brpc_channel_unittest.cpp | Adds regression tests for socket-map reference release on reinit and state preservation on failed reinit. |
| src/brpc/channel.h | Introduces internal single-server state storage (unique_ptr) and a reset helper for single-server lifecycle. |
| src/brpc/channel.cpp | Implements stored SocketMapKey lifecycle management and commits init state only after success to avoid partial overwrites. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…tion Channel reinitialization now retains the exact SocketMap key for each direct connection, so every successful insertion is balanced at teardown. Option initialization preserves its historic eager-commit behavior, including HTTPS peer-name derivation and failure followed by Init(..., NULL), while connection ownership is replaced only after setup succeeds.
416cdb9 to
9f169d0
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (3)
src/brpc/channel.cpp:497
Init(ns_url, lb_name, ...)commits_optionsand protocol function pointers before validatingclient_hostand beforelb->Init(). Ifclient_hostis invalid orlb->Init()fails,Init()returns-1but the channel’s options/protocol callbacks have already been overwritten while_scheme/_service_name/_lbremain unchanged. This creates a partially-updated channel configuration after a failed reinit.
Suggested fix: keep the parsed URL pieces and initialized_options local, and only assign _options/callbacks and swap _scheme/_service_name after lb->Init() succeeds (similar to how you defer ResetSingleServer() today).
_options = initialized_options.options;
_serialize_request = initialized_options.serialize_request;
_pack_request = initialized_options.pack_request;
_get_method_name = initialized_options.get_method_name;
_preferred_index = initialized_options.preferred_index;
src/brpc/channel.cpp:416
InitSingle()assigns_optionsand protocol function pointers before validatingserver_addr_and_port.port/client_hostand before the SocketMap insert+commit block. If any of the subsequent checks fail and the function returns-1, the channel keeps using the previously-initialized socket-map entry but its options are partially overwritten, leaving the channel in an inconsistent state after a failed reinitialization (contrary to the PR description’s “keep prior state intact on failure”).
Consider keeping initialized_options purely local and only committing _options/_serialize_request/etc after all validation succeeds and you’re ready to ResetSingleServer() + swap in the new state (or add a scoped rollback guard to restore the previous members on every early-return).
This issue also appears on line 493 of the same file.
_options = initialized_options.options;
_serialize_request = initialized_options.serialize_request;
_pack_request = initialized_options.pack_request;
_get_method_name = initialized_options.get_method_name;
_preferred_index = initialized_options.preferred_index;
test/brpc_channel_unittest.cpp:2353
- This test currently asserts that
channel.options().client_hostequals the invalidclient_hostfrom a failedInit()call. If the intent is to keep the prior channel state intact when reinitialization fails, options should remain unchanged after the failed init.
Capturing the original client_host and asserting it remains the same also avoids locking in inconsistent runtime behavior where options() no longer matches the socket that is actually in use.
EXPECT_EQ(endpoint, channel._server_address);
EXPECT_EQ(original_id, channel._server_id);
EXPECT_EQ(invalid_options.client_host, channel.options().client_host);
EXPECT_TRUE(channel.SingleServer());
What problem does this PR solve?
Issue Number: N/A
Problem Summary:
Reinitializing a Channel could leave stale SocketMap references because each successful direct initialization inserted a socket entry, while destruction released only the final derived key. Failed reinitialization could also partially overwrite the active Channel configuration.
What is changed and the side effects?
Changed:
Side effects:
———
Check List: