mirror of https://github.com/perkeep/perkeep.git
138 lines
2.8 KiB
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)
|
|
}
|