瀏覽代碼

Improve timeouts implementation

- use local attributes for timeouts
- do not type assert to net.Conn each time
- DSN params are CamelCase
Julien Schmidt 10 年之前
父節點
當前提交
68ca989dd4
共有 7 個文件被更改,包括 45 次插入40 次删除
  1. 5 7
      benchmark_test.go
  2. 15 14
      buffer.go
  3. 2 1
      connection.go
  4. 5 1
      driver.go
  5. 14 13
      dsn.go
  6. 1 1
      dsn_test.go
  7. 3 3
      packets.go

+ 5 - 7
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{

+ 15 - 14
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 {

+ 2 - 1
connection.go

@@ -24,6 +24,7 @@ type mysqlConn struct {
 	cfg              *Config
 	maxPacketAllowed int
 	maxWriteSize     int
+	writeTimeout     time.Duration
 	flags            clientFlag
 	status           statusFlag
 	sequence         uint8
@@ -98,7 +99,7 @@ func (mc *mysqlConn) cleanup() {
 		mc.netConn = nil
 	}
 	mc.cfg = nil
-	mc.buf.rd = nil
+	mc.buf.nc = nil
 }
 
 func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {

+ 5 - 1
driver.go

@@ -81,7 +81,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()

+ 14 - 13
dsn.go

@@ -243,6 +243,13 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				return errors.New("Invalid Bool value: " + value)
 			}
 
+		// I/O read Timeout
+		case "readTimeout":
+			cfg.ReadTimeout, err = time.ParseDuration(value)
+			if err != nil {
+				return
+			}
+
 		// Strict mode
 		case "strict":
 			var isBool bool
@@ -258,19 +265,6 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				return
 			}
 
-		// I/O Timeouts
-		case "read_timeout":
-			cfg.ReadTimeout, err = time.ParseDuration(value)
-			if err != nil {
-				return
-			}
-
-		case "write_timeout":
-			cfg.WriteTimeout, err = time.ParseDuration(value)
-			if err != nil {
-				return
-			}
-
 		// TLS-Encryption
 		case "tls":
 			boolValue, isBool := readBool(value)
@@ -297,6 +291,13 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				}
 			}
 
+		// I/O write Timeout
+		case "writeTimeout":
+			cfg.WriteTimeout, err = time.ParseDuration(value)
+			if err != nil {
+				return
+			}
+
 		default:
 			// lazy init
 			if cfg.Params == nil {

+ 1 - 1
dsn_test.go

@@ -24,7 +24,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:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
 	{"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:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
 	{"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:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
-	{"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:UTC TLS:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s Collation:224 AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
+	{"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:UTC TLS:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s Collation:224 AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
 	{"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:Local TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
 	{"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},
 	{"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Loc:UTC TLS:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 Collation:33 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false ParseTime:false Strict:false}"},

+ 3 - 3
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)