Skip to content

Commit

Permalink
Improve timeouts implementation
Browse files Browse the repository at this point in the history
- use local attributes for timeouts
- do not type assert to net.Conn each time
- DSN params are CamelCase
  • Loading branch information
julienschmidt committed Jan 19, 2016
1 parent de0bbfa commit 68ca989
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 40 deletions.
12 changes: 5 additions & 7 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
29 changes: 15 additions & 14 deletions buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

package mysql

import "io"
import "net"
import "time"
import (
"io"
"net"
"time"
)

const defaultBufSize = 4096

Expand All @@ -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,
}
}

Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type mysqlConn struct {
cfg *Config
maxPacketAllowed int
maxWriteSize int
writeTimeout time.Duration
flags clientFlag
status statusFlag
sequence uint8
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 5 additions & 1 deletion driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
27 changes: 14 additions & 13 deletions dsn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion dsn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}"},
Expand Down
6 changes: 3 additions & 3 deletions packets.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 68ca989

Please sign in to comment.