[client] Hand off dialed connections to the sweeper atomically

WrapDialContext and WrapConn registered the dial and the connection
independently, so a sweep landing between the dial finishing and WrapConn
cancelled only the dial registration: the connection dialed on the old
network entered the fresh registry and survived the network change.

Replace the pair with a Dial handle. Sweep marks pending dials under the
sweeper mutex, and WrapConn decides under the same mutex: a swept dial's
connection is closed and ErrSwept returned, so the caller redials on the
new network; otherwise the connection transfers to the registry with no
window in between.
This commit is contained in:
Zoltán Papp
2026-08-11 18:09:37 +02:00
parent 77e7d82d5a
commit 71bfc73cd1
4 changed files with 144 additions and 52 deletions
+4 -4
View File
@@ -28,14 +28,14 @@ func WithCustomDialer(_ bool, _ string) grpc.DialOption {
// dial options in order, so the later context dialer wins.
func WithSweeper(sweeper *netsweep.Sweeper) grpc.DialOption {
return grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
ctx, releaseDial := sweeper.WrapDialContext(ctx)
defer releaseDial()
dial := sweeper.StartDial(ctx)
defer dial.Release()
conn, err := dialContext(ctx, addr)
conn, err := dialContext(dial.Ctx(), addr)
if err != nil {
return nil, err
}
return sweeper.WrapConn(conn), nil
return dial.WrapConn(conn)
})
}