Skip to content

Commit ebb3476

Browse files
author
Phil Bayfield
committed
added auto reconnect, removed mutex lock/unlocking
1 parent ec666ff commit ebb3476

2 files changed

Lines changed: 69 additions & 17 deletions

File tree

README.markdown

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
GoMySQL Version 0.3.0-alpha-1
1+
GoMySQL Version 0.3.0-alpha-2
22
=============================
33

44

@@ -7,6 +7,7 @@ Revision History
77

88
0.3.x series [development]
99

10+
* 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.
1011
* 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.
1112

1213
0.2.x series [current]

mysql.go

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import (
1515
"net"
1616
"strings"
1717
"sync"
18+
"time"
1819
)
1920

2021
// Constants
2122
const (
2223
// General
23-
VERSION = "0.3.0-alpha-1"
24+
VERSION = "0.3.0-alpha-2"
2425
DEFAULT_PORT = "3306"
2526
DEFAULT_SOCKET = "/var/run/mysqld/mysqld.sock"
2627
MAX_PACKET_SIZE = 1<<24 - 1
@@ -69,6 +70,7 @@ type Client struct {
6970
r *reader
7071
w *writer
7172
connected bool
73+
Reconnect bool
7274

7375
// Sequence
7476
protocol uint8
@@ -134,9 +136,6 @@ func (c *Client) Connect(network, raddr, user, passwd string, dbname ...string)
134136
err = os.NewError("Already connected")
135137
return
136138
}
137-
// Lock mutex/defer unlock
138-
c.Lock()
139-
defer c.Unlock()
140139
// Reset client
141140
c.reset()
142141
// Store connection credentials
@@ -167,9 +166,6 @@ func (c *Client) Close() (err os.Error) {
167166
err = os.NewError("Must be connected to do this")
168167
return
169168
}
170-
// Lock mutex/defer unlock
171-
c.Lock()
172-
defer c.Unlock()
173169
// Reset client
174170
c.reset()
175171
// Send close command
@@ -185,6 +181,15 @@ func (c *Client) Close() (err os.Error) {
185181

186182
// Change the current database
187183
func (c *Client) ChangeDb(dbname string) (err os.Error) {
184+
// Auto reconnect
185+
defer func() {
186+
if err != nil && c.checkNet(err) && c.Reconnect {
187+
err = c.reconnect()
188+
if err == nil {
189+
err = c.ChangeDb(dbname)
190+
}
191+
}
192+
}()
188193
// Log changeDb
189194
c.log(1, "=== Begin change db to '%s' ===", dbname)
190195
// Pre-run checks
@@ -193,13 +198,13 @@ func (c *Client) ChangeDb(dbname string) (err os.Error) {
193198
err = os.NewError("Must be connected and not in a result set")
194199
return
195200
}
196-
// Lock mutex/defer unlock
197-
c.Lock()
198-
defer c.Unlock()
199201
// Reset client
200202
c.reset()
201203
// Send close command
202-
c.command(COM_INIT_DB, dbname)
204+
err = c.command(COM_INIT_DB, dbname)
205+
if err != nil {
206+
return
207+
}
203208
// Read result from server
204209
c.sequence++
205210
_, err = c.getResult(PACKET_OK | PACKET_ERROR)
@@ -208,6 +213,15 @@ func (c *Client) ChangeDb(dbname string) (err os.Error) {
208213

209214
// Send a query/queries to the server
210215
func (c *Client) Query(sql string) (err os.Error) {
216+
// Auto reconnect
217+
defer func() {
218+
if err != nil && c.checkNet(err) && c.Reconnect {
219+
err = c.reconnect()
220+
if err == nil {
221+
err = c.Query(sql)
222+
}
223+
}
224+
}()
211225
// Log query
212226
c.log(1, "=== Begin query '%s' ===", sql)
213227
// Pre-run checks
@@ -216,13 +230,13 @@ func (c *Client) Query(sql string) (err os.Error) {
216230
err = os.NewError("Must be connected and not in a result set")
217231
return
218232
}
219-
// Lock mutex/defer unlock
220-
c.Lock()
221-
defer c.Unlock()
222233
// Reset client
223234
c.reset()
224235
// Send close command
225-
c.command(COM_QUERY, sql)
236+
err = c.command(COM_QUERY, sql)
237+
if err != nil {
238+
return
239+
}
226240
// Read result from server
227241
c.sequence++
228242
_, err = c.getResult(PACKET_OK | PACKET_ERROR | PACKET_RESULT)
@@ -522,7 +536,7 @@ func (c *Client) reset() {
522536
}
523537

524538
// Check if connected
525-
// @todo expand to perform an actual connection check?
539+
// @todo expand to perform an actual connection check
526540
func (c *Client) checkConn() bool {
527541
if c.connected {
528542
return true
@@ -695,6 +709,43 @@ func (c *Client) auth() (err os.Error) {
695709
return
696710
}
697711

712+
// Check if a network error occurred
713+
func (c *Client) checkNet(err os.Error) bool {
714+
// EOF check
715+
if err == os.EOF || err == io.ErrUnexpectedEOF {
716+
c.log(1, "!!! Lost connection to server !!!")
717+
c.error(CR_SERVER_GONE_ERROR, CR_SERVER_GONE_ERROR_STR)
718+
c.connected = false
719+
return true
720+
}
721+
// OpError check
722+
if _, ok := err.(*net.OpError); ok {
723+
c.log(1, "!!! Lost connection to server !!!")
724+
c.error(CR_SERVER_LOST, CR_SERVER_LOST_STR)
725+
c.connected = false
726+
return true
727+
}
728+
return false
729+
}
730+
731+
// Perform reconnect if a network error occurs
732+
func (c *Client) reconnect() (err os.Error) {
733+
// Log auto reconnect
734+
c.log(1, "=== Begin auto reconnect attempt ===")
735+
// Reset the client
736+
c.reset()
737+
// Attempt to reconnect
738+
for i := 0; i < 10; i ++ {
739+
err = c.connect()
740+
if err == nil {
741+
c.connected = true
742+
break
743+
}
744+
time.Sleep(2000000000)
745+
}
746+
return
747+
}
748+
698749
// Send a command to the server
699750
func (c *Client) command(command command, args ...interface{}) (err os.Error) {
700751
// Log write packet

0 commit comments

Comments
 (0)