perkeep/third_party/github.com/ziutek/mymysql/autorc/autorecon.go

138 lines
2.8 KiB
Go

// Auto reconnect interface for MyMySQL
package autorc
import (
"camlistore.org/third_party/github.com/ziutek/mymysql/mysql"
"io"
"log"
"net"
"time"
)
// Return true if error is network error or UnexpectedEOF.
func IsNetErr(err error) bool {
if err == io.ErrUnexpectedEOF {
return true
} else if e, ok := err.(net.Error); ok && e.Temporary() {
return true
}
return false
}
type Conn struct {
Raw mysql.Conn
// Maximum reconnect retries.
// Default is 7 which means 1+2+3+4+5+6+7 = 28 seconds before return error.
MaxRetries int
// Debug logging. You may change it at any time.
Debug bool
}
func New(proto, laddr, raddr, user, passwd string, db ...string) *Conn {
return &Conn{mysql.New(proto, laddr, raddr, user, passwd, db...), 7, false}
}
func (c *Conn) reconnectIfNetErr(nn *int, err *error) {
for *err != nil && IsNetErr(*err) && *nn <= c.MaxRetries {
if c.Debug {
log.Printf("Error: '%s' - reconnecting...", *err)
}
time.Sleep(1e9 * time.Duration(*nn))
*err = c.Raw.Reconnect()
if c.Debug && *err != nil {
log.Println("Can't reconnect:", *err)
}
*nn++
}
}
func (c *Conn) connectIfNotConnected() (err error) {
if c.Raw.IsConnected() {
return
}
err = c.Raw.Connect()
nn := 0
c.reconnectIfNetErr(&nn, &err)
return
}
// Automatic connect/reconnect/repeat version of Use
func (c *Conn) Use(dbname string) (err error) {
if err = c.connectIfNotConnected(); err != nil {
return
}
nn := 0
for {
if err = c.Raw.Use(dbname); err == nil {
return
}
if c.reconnectIfNetErr(&nn, &err); err != nil {
return
}
}
panic(nil)
}
// Automatic connect/reconnect/repeat version of Query
func (c *Conn) Query(sql string, params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) {
if err = c.connectIfNotConnected(); err != nil {
return
}
nn := 0
for {
if rows, res, err = c.Raw.Query(sql, params...); err == nil {
return
}
if c.reconnectIfNetErr(&nn, &err); err != nil {
return
}
}
panic(nil)
}
type Stmt struct {
Raw mysql.Stmt
con *Conn
}
// Automatic connect/reconnect/repeat version of Prepare
func (c *Conn) Prepare(sql string) (*Stmt, error) {
if err := c.connectIfNotConnected(); err != nil {
return nil, err
}
nn := 0
for {
var (
err error
s mysql.Stmt
)
if s, err = c.Raw.Prepare(sql); err == nil {
return &Stmt{s, c}, nil
}
if c.reconnectIfNetErr(&nn, &err); err != nil {
return nil, err
}
}
panic(nil)
}
// Automatic connect/reconnect/repeat version of Exec
func (s *Stmt) Exec(params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) {
if err = s.con.connectIfNotConnected(); err != nil {
return
}
nn := 0
for {
if rows, res, err = s.Raw.Exec(params...); err == nil {
return
}
if s.con.reconnectIfNetErr(&nn, &err); err != nil {
return
}
}
panic(nil)
}