Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added auto reconnect, removed mutex lock/unlocking

  • Loading branch information...
commit ebb347602bfbd229b70ef45ecdb41ebcd42f75d7 1 parent ec666ff
@Philio authored
Showing with 69 additions and 17 deletions.
  1. +2 −1  README.markdown
  2. +67 −16 mysql.go
View
3  README.markdown
@@ -1,4 +1,4 @@
-GoMySQL Version 0.3.0-alpha-1
+GoMySQL Version 0.3.0-alpha-2
=============================
@@ -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]
View
83 mysql.go
@@ -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
@@ -69,6 +70,7 @@ type Client struct {
r *reader
w *writer
connected bool
+ Reconnect bool
// Sequence
protocol uint8
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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)
@@ -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
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.