Skip to content

conn: return a close function from NewConfig and remove Config.Close - #352

Merged
meling merged 2 commits into
masterfrom
feature/314/config-close-func
Oct 10, 2026
Merged

meling merged 2 commits into
masterfrom
feature/314/config-close-func

Conversation

@meling

@meling meling commented Oct 10, 2026

Copy link
Copy Markdown
Member

Closes #314

Problem

All configurations derived from one NewConfig call share one connection pool. Config.Close closed that whole pool from any configuration in it, so cfg.Remove(3).Close() also closed node 3. #351 documented this rule and fixed the leak and the data race around it, but the rule still surprises users.

A Config is a plain []*Node. It cannot record whether it owns the pool, so no rule for Config.Close can avoid the surprise. Reference counting on Node does not work either; see the decision comment on #314.

Change

NewConfig returns a close function, and Config has no Close method:

cfg, closeFn, err := gorums.NewConfig(gorums.WithNodeList(addrs), dialOpts...)
if err != nil {
	return err
}
defer closeFn()
  • Only the creator holds the close function. Derived configurations (Extend, Remove, Sort, WithoutErrors, slicing) are views and cannot close the pool.
  • The close function closes every node in the pool, including nodes that Extend added. It is idempotent and safe for concurrent use.
  • It returns no error. The only error the close path can produce is gRPC's deprecated ErrClientConnClosing, for a connection that is already closed, and Gorums closes each connection once. The manager logs a node's close error if a logger is set.
  • On error, NewConfig returns a nil configuration and a nil close function.
  • Server keeps the close function for its WithPeers configuration and calls it in Stop and GracefulStop. A handler can no longer close the server's peer pool through ServerContext.PeerConfig().
  • gorumstest.Closer existed only to check the close error, so it is removed. Tests pass the close function to t.Cleanup directly.
  • The internals from conn: reject new nodes after Close and lock the node list in Close #351 are unchanged: the closed flag, the locking, and the addNode and Extend checks.

This is a breaking API change. No release has been made since v0.11.0.

Testing

  • TestConfigClose is rewritten around the close function:
    • ClosesExtendedNodes: nodes that Extend added are closed too.
    • Idempotent: concurrent calls all return after the nodes are closed.
    • ExtendAfterClose: new node, existing node, and nil source all fail.
    • NewConfigError: a failing NewConfig returns nil, nil and leaks nothing (goleak). Removing the cleanup on the error path makes this subtest fail.
    • ConcurrentExtend: unchanged.
  • TestNewConfig and TestNewConfigWithBackChannel also check that both results are nil on error.
  • go test ./... passes in the root, benchkit, and examples modules.
  • go test -race -count=5 . ./internal/conn/ ./gorumstest/ passes.
  • go test -tags=integration ./... passes.
  • make modernize and make goplscheck are clean for the changed files.

Copilot AI balanced review requested due to automatic review settings October 10, 2026 11:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Two guide examples omit cleanup, and the documented error-result assertion is missing from the back-channel test.

2 open findings
What changed in this PR

Moves connection-pool ownership from Config views to a close function returned by NewConfig.

Changes:

  • Removes Config.Close and updates all callers.
  • Makes pool shutdown idempotent, concurrent-safe, and server-owned for peers.
  • Updates lifecycle tests and documentation.
File Description
config.go Exposes the new NewConfig signature.
config_test.go Tests pool closure and error behavior.
internal/​conn/​config.go Returns the pool close function.
internal/​conn/​config_test.go Removes obsolete close tests.
internal/​conn/​outbound_manager.go Logs close errors and returns no error.
server.go Stores and invokes peer-pool cleanup.
server_test.go Migrates test cleanup calls.
server_internal_test.go Migrates internal peer helpers.
gorumstest/​gorumstest.go Removes Closer and registers close functions.
gorumstest/​servers.go Updates example usage.
examples/​storage/​client.go Uses the returned close function.
benchkit/​benchmark/​target.go Returns the new cleanup function.
benchkit/​benchmark/​benchmark_test.go Migrates benchmark test cleanup.
doc/​user-guide.md Documents the ownership model.
doc/​dev-guide.md Updates internal lifecycle documentation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread config_test.go
Comment thread doc/user-guide.md
Config.Close closed the whole connection pool from any configuration
in it, so cfg.Remove(3).Close() also closed node 3. A Config is a plain
node slice and cannot record whether it owns the pool, so no rule on
Config.Close can avoid this.

NewConfig now returns a func() that closes the pool, and Config has no
Close method. Only the creator holds the close function; derived
configurations are views that cannot close the pool. The close function
returns no error: the only error the close path can produce is gRPC's
ErrClientConnClosing for a connection that is already closed, and each
connection is closed once. The manager logs a node's close error with
its logger instead.

Server keeps the close function for its peer configuration and calls
it in Stop and GracefulStop. gorumstest.Closer existed only to check
the close error, so it is removed; tests pass the close function to
t.Cleanup directly.
@meling
meling force-pushed the feature/314/config-close-func branch from 64f15d5 to e89e325 Compare October 10, 2026 11:54
@meling
meling merged commit 47c56c8 into master Oct 10, 2026
3 checks passed
@meling
meling deleted the feature/314/config-close-func branch October 10, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

task: clarify Close semantics for Configuration and Node

2 participants