diff --git a/benchmark_test.go b/benchmark_test.go index 40d5c9df4..fb8a2f5f3 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -215,16 +215,14 @@ func BenchmarkRoundtripBin(b *testing.B) { } func BenchmarkInterpolation(b *testing.B) { - cfg := &config{ - interpolateParams: true, - loc: time.UTC, - } - mc := &mysqlConn{ - cfg: cfg, + cfg: &config{ + interpolateParams: true, + loc: time.UTC, + }, maxPacketAllowed: maxPacketSize, maxWriteSize: maxPacketSize - 1, - buf: newBuffer(nil, cfg), + buf: newBuffer(nil), } args := []driver.Value{ diff --git a/buffer.go b/buffer.go index 660ee4ce8..2001feacd 100644 --- a/buffer.go +++ b/buffer.go @@ -8,9 +8,11 @@ package mysql -import "io" -import "net" -import "time" +import ( + "io" + "net" + "time" +) const defaultBufSize = 4096 @@ -20,19 +22,18 @@ const defaultBufSize = 4096 // The buffer is similar to bufio.Reader / Writer but zero-copy-ish // Also highly optimized for this particular use case. type buffer struct { - buf []byte - rd io.Reader - idx int - length int - cfg *config + buf []byte + nc net.Conn + idx int + length int + timeout time.Duration } -func newBuffer(rd io.Reader, cfg *config) buffer { +func newBuffer(nc net.Conn) buffer { var b [defaultBufSize]byte return buffer{ buf: b[:], - rd: rd, - cfg: cfg, + nc: nc, } } @@ -58,13 +59,13 @@ func (b *buffer) fill(need int) error { b.idx = 0 for { - if conn, ok := b.rd.(net.Conn); ok && b.cfg.readTimeout > 0 { - if err := conn.SetReadDeadline(time.Now().Add(b.cfg.readTimeout)); err != nil { + if b.timeout > 0 { + if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil { return err } } - nn, err := b.rd.Read(b.buf[n:]) + nn, err := b.nc.Read(b.buf[n:]) n += nn switch err { diff --git a/connection.go b/connection.go index 53bd7ac7d..5fb093d71 100644 --- a/connection.go +++ b/connection.go @@ -26,6 +26,7 @@ type mysqlConn struct { cfg *config maxPacketAllowed int maxWriteSize int + writeTimeout time.Duration flags clientFlag status statusFlag sequence uint8 @@ -131,7 +132,7 @@ func (mc *mysqlConn) Close() (err error) { } mc.cfg = nil - mc.buf.rd = nil + mc.buf.nc = nil return } diff --git a/driver.go b/driver.go index e5967a0e7..b492031b1 100644 --- a/driver.go +++ b/driver.go @@ -79,7 +79,11 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { } } - mc.buf = newBuffer(mc.netConn, mc.cfg) + mc.buf = newBuffer(mc.netConn) + + // Set I/O timeouts + mc.buf.timeout = mc.cfg.readTimeout + mc.writeTimeout = mc.cfg.writeTimeout // Reading Handshake Initialization Packet cipher, err := mc.readInitPacket() diff --git a/packets.go b/packets.go index c3eb31a23..eeff18623 100644 --- a/packets.go +++ b/packets.go @@ -100,8 +100,8 @@ func (mc *mysqlConn) writePacket(data []byte) error { data[3] = mc.sequence // Write packet - if mc.cfg.writeTimeout > 0 { - if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.cfg.writeTimeout)); err != nil { + if mc.writeTimeout > 0 { + if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil { return err } } @@ -284,7 +284,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { return err } mc.netConn = tlsConn - mc.buf.rd = tlsConn + mc.buf.nc = tlsConn } // Filler [23 bytes] (all 0x00) diff --git a/utils.go b/utils.go index 80c34ad7b..9ac69b249 100644 --- a/utils.go +++ b/utils.go @@ -260,14 +260,15 @@ func parseDSNParams(cfg *config, params string) (err error) { return } - // I/O Timeouts - case "read_timeout": + // I/O Read Timeout + case "readTimeout": cfg.readTimeout, err = time.ParseDuration(value) if err != nil { return } - case "write_timeout": + // I/O Write Timeout + case "writeTimeout": cfg.writeTimeout, err = time.ParseDuration(value) if err != nil { return diff --git a/utils_test.go b/utils_test.go index 01cc0e20f..d86496a8f 100644 --- a/utils_test.go +++ b/utils_test.go @@ -27,7 +27,7 @@ var testDSNs = []struct { {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, - {"user:password@/dbname?loc=UTC&timeout=30s&read_timeout=1s&write_timeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls: timeout:30000000000 readTimeout:1000000000 writeTimeout:1000000000 collation:224 allowAllFiles:true allowOldPasswords:true allowCleartextPasswords:false clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC}, + {"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls: timeout:30000000000 readTimeout:1000000000 writeTimeout:1000000000 collation:224 allowAllFiles:true allowOldPasswords:true allowCleartextPasswords:false clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC}, {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.Local}, {"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC}, {"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls: timeout:0 readTimeout:0 writeTimeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},