From a9189a7ec9491c416b8656c778146be33eb9b562 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 7 May 2019 09:56:04 +0200 Subject: [PATCH 1/2] packets: reset read deadline before conn check If a MySQL connection has been configured with a short `ReadTimeout`, each read from the TCP connection will be preceded by a `SetReadDeadline` call, which lingers until the next `SetReadDeadline`. This can be an issue if the connection becomes stale after staying too long in the connection pool, because when we attempt to perform a stale connection check, the Go runtime scheduler will return a timedout error from the scheduler itself, without letting us get to the kernel to perform the non-blocking read. To fix this, reset the read deadline before we perform the connection check. --- packets.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packets.go b/packets.go index cbed325f4..bf538bc52 100644 --- a/packets.go +++ b/packets.go @@ -108,7 +108,17 @@ func (mc *mysqlConn) writePacket(data []byte) error { if mc.rawConn != nil { conn = mc.rawConn } - if err := connCheck(conn); err != nil { + var err error + // If this connection has a ReadTimeout which we've been setting on + // reads, reset it to its default value before we attempt a non-blocking + // read, otherwise the scheduler will just time us out before we can read + if mc.cfg.ReadTimeout != 0 { + err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout)) + } + if err == nil { + err = connCheck(conn) + } + if err != nil { errLog.Print("closing bad idle connection: ", err) mc.Close() return driver.ErrBadConn From 6ee19d40fdeee1a830a794033f546d2d30867ca8 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Tue, 7 May 2019 10:39:50 +0200 Subject: [PATCH 2/2] packets: set a 0 deadline --- packets.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packets.go b/packets.go index bf538bc52..30b3352c2 100644 --- a/packets.go +++ b/packets.go @@ -113,7 +113,7 @@ func (mc *mysqlConn) writePacket(data []byte) error { // reads, reset it to its default value before we attempt a non-blocking // read, otherwise the scheduler will just time us out before we can read if mc.cfg.ReadTimeout != 0 { - err = conn.SetReadDeadline(time.Now().Add(mc.cfg.ReadTimeout)) + err = conn.SetReadDeadline(time.Time{}) } if err == nil { err = connCheck(conn)