Skip to content

Commit

Permalink
added auto reconnect, removed mutex lock/unlocking
Browse files Browse the repository at this point in the history
  • Loading branch information
Phil Bayfield committed Feb 8, 2011
1 parent ec666ff commit ebb3476
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GoMySQL Version 0.3.0-alpha-1
GoMySQL Version 0.3.0-alpha-2
=============================


Expand All @@ -7,6 +7,7 @@ Revision History

0.3.x series [development]

* 0.3.0-alpha-2 - Second test relese of new library. Added transaction wrappers, Added auto-reconnect functionality to repeatable methods. Removed mutex lock/unlocking, as it is now more appropriate that the application decides when thread safe functions are required and it's considerably safer to have a sequence such as Client.Lock(), Client.Query(...), Client.Unlock(). Added a new test which performs create, drop, select, insert and update queries on a simple demo table to test the majority of the library functionality. Added additional error messages to places where an error could be returned but there was no error number/string set. Many small changes and general improvements.
* 0.3.0-alpha-1 - First test release of new library, completely rewritten from scratch. Fully compatible with all versions of MySQL using the 4.1+ protocol and 4.0 protocol (which supports earlier versions). Fully supports old and new passwords, including old passwords using the 4.1 protocol. Includes new Go style constructors 'NewClient', 'DialTCP', 'DialUnix' replacing 'New' from the 0.2 branch. All structs have been renamed to be more user friendly, MySQL has also now been replaced with Client. Removed many dependencies on external packages such as bufio. New reader that reads the entire packet completely to a slice then processes afterwards. New writer that constructs the entire packet completely to a slice and writes in a single operation. The Client.Query function no longer returns a result set and now uses the tradition store/use result mechanism for retrieving the result and processing it's contents. The 'MultiQuery' function has been removed as this is now supported by the Client.Query function. Currently all result sets must be freed before another query can be executed either using the Result.Free() method or Client.FreeResult() method, a check for additional result sets can be made using Client.MoreResults() and the next result can be retrieved using Client.NextResult(). Client.FreeResult() is capable of reading and discarding an entire result set (provided the first result set packet has been read), a partially read result set (e.g. from Client.UseResult) or a fully stored result. Transaction support and prepared statements are NOT available in this alpha release.

0.2.x series [current]
Expand Down
83 changes: 67 additions & 16 deletions mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import (
"net"
"strings"
"sync"
"time"
)

// Constants
const (
// General
VERSION = "0.3.0-alpha-1"
VERSION = "0.3.0-alpha-2"
DEFAULT_PORT = "3306"
DEFAULT_SOCKET = "/var/run/mysqld/mysqld.sock"
MAX_PACKET_SIZE = 1<<24 - 1
Expand Down Expand Up @@ -69,6 +70,7 @@ type Client struct {
r *reader
w *writer
connected bool
Reconnect bool

// Sequence
protocol uint8
Expand Down Expand Up @@ -134,9 +136,6 @@ func (c *Client) Connect(network, raddr, user, passwd string, dbname ...string)
err = os.NewError("Already connected")
return
}
// Lock mutex/defer unlock
c.Lock()
defer c.Unlock()
// Reset client
c.reset()
// Store connection credentials
Expand Down Expand Up @@ -167,9 +166,6 @@ func (c *Client) Close() (err os.Error) {
err = os.NewError("Must be connected to do this")
return
}
// Lock mutex/defer unlock
c.Lock()
defer c.Unlock()
// Reset client
c.reset()
// Send close command
Expand All @@ -185,6 +181,15 @@ func (c *Client) Close() (err os.Error) {

// Change the current database
func (c *Client) ChangeDb(dbname string) (err os.Error) {
// Auto reconnect
defer func() {
if err != nil && c.checkNet(err) && c.Reconnect {
err = c.reconnect()
if err == nil {
err = c.ChangeDb(dbname)
}
}
}()
// Log changeDb
c.log(1, "=== Begin change db to '%s' ===", dbname)
// Pre-run checks
Expand All @@ -193,13 +198,13 @@ func (c *Client) ChangeDb(dbname string) (err os.Error) {
err = os.NewError("Must be connected and not in a result set")
return
}
// Lock mutex/defer unlock
c.Lock()
defer c.Unlock()
// Reset client
c.reset()
// Send close command
c.command(COM_INIT_DB, dbname)
err = c.command(COM_INIT_DB, dbname)
if err != nil {
return
}
// Read result from server
c.sequence++
_, err = c.getResult(PACKET_OK | PACKET_ERROR)
Expand All @@ -208,6 +213,15 @@ func (c *Client) ChangeDb(dbname string) (err os.Error) {

// Send a query/queries to the server
func (c *Client) Query(sql string) (err os.Error) {
// Auto reconnect
defer func() {
if err != nil && c.checkNet(err) && c.Reconnect {
err = c.reconnect()
if err == nil {
err = c.Query(sql)
}
}
}()
// Log query
c.log(1, "=== Begin query '%s' ===", sql)
// Pre-run checks
Expand All @@ -216,13 +230,13 @@ func (c *Client) Query(sql string) (err os.Error) {
err = os.NewError("Must be connected and not in a result set")
return
}
// Lock mutex/defer unlock
c.Lock()
defer c.Unlock()
// Reset client
c.reset()
// Send close command
c.command(COM_QUERY, sql)
err = c.command(COM_QUERY, sql)
if err != nil {
return
}
// Read result from server
c.sequence++
_, err = c.getResult(PACKET_OK | PACKET_ERROR | PACKET_RESULT)
Expand Down Expand Up @@ -522,7 +536,7 @@ func (c *Client) reset() {
}

// Check if connected
// @todo expand to perform an actual connection check?
// @todo expand to perform an actual connection check
func (c *Client) checkConn() bool {
if c.connected {
return true
Expand Down Expand Up @@ -695,6 +709,43 @@ func (c *Client) auth() (err os.Error) {
return
}

// Check if a network error occurred
func (c *Client) checkNet(err os.Error) bool {
// EOF check
if err == os.EOF || err == io.ErrUnexpectedEOF {
c.log(1, "!!! Lost connection to server !!!")
c.error(CR_SERVER_GONE_ERROR, CR_SERVER_GONE_ERROR_STR)
c.connected = false
return true
}
// OpError check
if _, ok := err.(*net.OpError); ok {
c.log(1, "!!! Lost connection to server !!!")
c.error(CR_SERVER_LOST, CR_SERVER_LOST_STR)
c.connected = false
return true
}
return false
}

// Perform reconnect if a network error occurs
func (c *Client) reconnect() (err os.Error) {
// Log auto reconnect
c.log(1, "=== Begin auto reconnect attempt ===")
// Reset the client
c.reset()
// Attempt to reconnect
for i := 0; i < 10; i ++ {
err = c.connect()
if err == nil {
c.connected = true
break
}
time.Sleep(2000000000)
}
return
}

// Send a command to the server
func (c *Client) command(command command, args ...interface{}) (err os.Error) {
// Log write packet
Expand Down

0 comments on commit ebb3476

Please sign in to comment.