From 8e109e2f2d7158c4a75343b82bd17cad78ce8a95 Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Thu, 7 May 2026 16:03:12 +0300 Subject: [PATCH] fix(pool): Fix Close. Do healthchecks only on idle conns --- internal/pool/conn.go | 6 ++++-- internal/pool/pool.go | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/pool/conn.go b/internal/pool/conn.go index 6abb58739..fab54654a 100644 --- a/internal/pool/conn.go +++ b/internal/pool/conn.go @@ -82,8 +82,10 @@ type Conn struct { bw *bufio.Writer wr *proto.Writer - // Lightweight mutex to protect reader operations during handoff - // Only used for the brief period during SetNetConn and HasBufferedData/PeekReplyTypeSafe + // Lightweight mutex to protect reader operations during handoff and health checks + // Used during: + // - SetNetConn (write lock for resetting reader state) + // - HasBufferedData/PeekReplyTypeSafe (read lock for safe concurrent peek operations) readerMu sync.RWMutex // State machine for connection state management diff --git a/internal/pool/pool.go b/internal/pool/pool.go index 19381c313..a324475ed 100644 --- a/internal/pool/pool.go +++ b/internal/pool/pool.go @@ -1616,9 +1616,19 @@ func (p *ConnPool) Close() error { // Check health before closing, since closeConn invalidates the // underlying fd and would make connCheck (inside isHealthyConn) // always fail with EBADF. - healthy := p.isHealthyConn(cn, nowNs) + // Only check health for idle connections to avoid data races when + // peeking at the socket/reader while another goroutine is reading from it. + // Non-idle connections are either in use or in transitional states and + // shouldn't be health-checked during shutdown. + _, isIdle := idleSet[cn.GetID()] + var healthy bool + if isIdle { + healthy = p.isHealthyConn(cn, nowNs) + } else { + healthy = true + } if cb != nil { - if _, isIdle := idleSet[cn.GetID()]; isIdle { + if isIdle { cb(ctx, -1, cn, "idle", false) } else { cb(ctx, -1, cn, "used", false)