Skip to content

Commit 950c25f

Browse files
mgatnycbusbey
authored andcommitted
Expose sql.DB's SetConnMaxLifetime in settings (quickfixgo#144)
When using a database server that closes inactive connections after some duration, set `SQLConnMaxLifetime` to a value less than that duration. Otherwise, the connection will be reused forever, and this could result in a dropped connection. For example, if you are using MySQL configured with `wait_timeout = 28800`, use something like `SQLConnMaxLifetime=14400s`. Refs quickfixgo#139
1 parent 522dda4 commit 950c25f

File tree

5 files changed

+46
-11
lines changed

5 files changed

+46
-11
lines changed

config/configuration.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ const (
1818
FileStorePath string = "FileStorePath"
1919
SQLDriver string = "SQLDriver"
2020
SQLDataSourceName string = "SQLDataSourceName"
21+
SQLConnMaxLifetime string = "SQLConnMaxLifetime"
2122
ValidateFieldsOutOfOrder string = "ValidateFieldsOutOfOrder"
2223
)

config/doc.go

+10
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,15 @@ The name of the database driver to use (see https://github.com/golang/go/wiki/SQ
138138
SQLDataSourceName
139139
140140
The driver-specific data source name of the database to use. Only used with SqlStoreFactory.
141+
142+
SQLConnMaxLifetime
143+
144+
SetConnMaxLifetime sets the maximum duration of time that a database connection may be reused (see https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime). Defaults to zero, which causes connections to be reused forever. Only used with SqlStoreFactory.
145+
146+
If your database server has a config option to close inactive connections after some duration (e.g. MySQL "wait_timeout"), set SQLConnMaxLifetime to a value less than that duration.
147+
148+
Example Values:
149+
SQLConnMaxLifetime=14400s # 14400 seconds
150+
SQLConnMaxLifetime=2h45m # 2 hours and 45 minutes
141151
*/
142152
package config

session_settings.go

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"strconv"
7+
"time"
78
)
89

910
//SessionSettings maps session settings to values with typed accessors.
@@ -61,6 +62,17 @@ func (s *SessionSettings) IntSetting(setting string) (int, error) {
6162
return strconv.Atoi(stringVal)
6263
}
6364

65+
//DurationSetting returns the requested setting parsed as a time.Duration. Returns an error if the setting is not set or cannot be parsed as a time.Duration.
66+
func (s *SessionSettings) DurationSetting(setting string) (val time.Duration, err error) {
67+
stringVal, err := s.Setting(setting)
68+
69+
if err != nil {
70+
return
71+
}
72+
73+
return time.ParseDuration(stringVal)
74+
}
75+
6476
//BoolSetting returns the requested setting parsed as a boolean. Returns an errror if the setting is not set or cannot be parsed as a bool.
6577
func (s SessionSettings) BoolSetting(setting string) (bool, error) {
6678
stringVal, err := s.Setting(setting)

sqlstore.go

+22-11
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ type sqlStoreFactory struct {
1313
}
1414

1515
type sqlStore struct {
16-
sessionID SessionID
17-
cache *memoryStore
18-
sqlDriver string
19-
sqlDataSourceName string
20-
db *sql.DB
16+
sessionID SessionID
17+
cache *memoryStore
18+
sqlDriver string
19+
sqlDataSourceName string
20+
sqlConnMaxLifetime time.Duration
21+
db *sql.DB
2122
}
2223

2324
// NewSQLStoreFactory returns a sql-based implementation of MessageStoreFactory
@@ -39,21 +40,31 @@ func (f sqlStoreFactory) Create(sessionID SessionID) (msgStore MessageStore, err
3940
if err != nil {
4041
return nil, err
4142
}
42-
return newSQLStore(sessionID, sqlDriver, sqlDataSourceName)
43+
sqlConnMaxLifetime := 0 * time.Second
44+
if sessionSettings.HasSetting(config.SQLConnMaxLifetime) {
45+
sqlConnMaxLifetime, err = sessionSettings.DurationSetting(config.SQLConnMaxLifetime)
46+
if err != nil {
47+
return nil, err
48+
}
49+
}
50+
return newSQLStore(sessionID, sqlDriver, sqlDataSourceName, sqlConnMaxLifetime)
4351
}
4452

45-
func newSQLStore(sessionID SessionID, driver string, dataSourceName string) (store *sqlStore, err error) {
53+
func newSQLStore(sessionID SessionID, driver string, dataSourceName string, connMaxLifetime time.Duration) (store *sqlStore, err error) {
4654
store = &sqlStore{
47-
sessionID: sessionID,
48-
cache: &memoryStore{},
49-
sqlDriver: driver,
50-
sqlDataSourceName: dataSourceName,
55+
sessionID: sessionID,
56+
cache: &memoryStore{},
57+
sqlDriver: driver,
58+
sqlDataSourceName: dataSourceName,
59+
sqlConnMaxLifetime: connMaxLifetime,
5160
}
5261
store.cache.Reset()
5362

5463
if store.db, err = sql.Open(store.sqlDriver, store.sqlDataSourceName); err != nil {
5564
return nil, err
5665
}
66+
store.db.SetConnMaxLifetime(store.sqlConnMaxLifetime)
67+
5768
if err = store.db.Ping(); err != nil { // ensure immediate connection
5869
return nil, err
5970
}

sqlstore_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func (suite *SQLStoreTestSuite) SetupTest() {
4747
[DEFAULT]
4848
SQLDriver=%s
4949
SQLDataSourceName=%s
50+
SQLConnMaxLifetime=14400s
5051
5152
[SESSION]
5253
BeginString=%s

0 commit comments

Comments
 (0)