Use retry func while trying to ping SQL datastore (#630)

* Use retry func while trying to ping SQL datastore

 - implements retry func specifically for SQL datastore ping
 - fmt fixes
 - using sqlx.Db.PingContext instead of sqlx.Db.Ping
 - propogate context to SQL datastore

* Rely on context from ServerOpt

* Consolidate log instances

* Cleanup

* Fix server usage in API tests
This commit is contained in:
Denis Makogon
2018-01-02 20:32:10 +02:00
committed by Reed Allman
parent 4a85806544
commit faaf5846ce
8 changed files with 117 additions and 66 deletions

View File

@@ -13,6 +13,7 @@ import (
"strings"
"time"
"github.com/fnproject/fn/api/common"
"github.com/fnproject/fn/api/datastore/sql/migrations"
"github.com/fnproject/fn/api/models"
"github.com/go-sql-driver/mysql"
@@ -96,14 +97,15 @@ type sqlStore struct {
// New will open the db specified by url, create any tables necessary
// and return a models.Datastore safe for concurrent usage.
func New(url *url.URL) (models.Datastore, error) {
return newDS(url)
func New(ctx context.Context, url *url.URL) (models.Datastore, error) {
return newDS(ctx, url)
}
// for test methods, return concrete type, but don't expose
func newDS(url *url.URL) (*sqlStore, error) {
func newDS(ctx context.Context, url *url.URL) (*sqlStore, error) {
driver := url.Scheme
log := common.Logger(ctx)
// driver must be one of these for sqlx to work, double check:
switch driver {
case "postgres", "pgx", "mysql", "sqlite3":
@@ -128,25 +130,25 @@ func newDS(url *url.URL) (*sqlStore, error) {
sqldb, err := sql.Open(driver, uri)
if err != nil {
logrus.WithFields(logrus.Fields{"url": uri}).WithError(err).Error("couldn't open db")
log.WithFields(logrus.Fields{"url": uri}).WithError(err).Error("couldn't open db")
return nil, err
}
db := sqlx.NewDb(sqldb, driver)
// force a connection and test that it worked
err = db.Ping()
err = pingWithRetry(ctx, 10, time.Second*1, db)
if err != nil {
logrus.WithFields(logrus.Fields{"url": uri}).WithError(err).Error("couldn't ping db")
log.WithFields(logrus.Fields{"url": uri}).WithError(err).Error("couldn't ping db")
return nil, err
}
maxIdleConns := 256 // TODO we need to strip this out of the URL probably
db.SetMaxIdleConns(maxIdleConns)
logrus.WithFields(logrus.Fields{"max_idle_connections": maxIdleConns, "datastore": driver}).Info("datastore dialed")
log.WithFields(logrus.Fields{"max_idle_connections": maxIdleConns, "datastore": driver}).Info("datastore dialed")
err = runMigrations(url.String(), checkExistence(db)) // original url string
if err != nil {
logrus.WithError(err).Error("error running migrations")
log.WithError(err).Error("error running migrations")
return nil, err
}
@@ -155,7 +157,7 @@ func newDS(url *url.URL) (*sqlStore, error) {
db.SetMaxOpenConns(1)
}
for _, v := range tables {
_, err = db.Exec(v)
_, err = db.ExecContext(ctx, v)
if err != nil {
return nil, err
}
@@ -164,6 +166,17 @@ func newDS(url *url.URL) (*sqlStore, error) {
return &sqlStore{db: db}, nil
}
func pingWithRetry(ctx context.Context, attempts int, sleep time.Duration, db *sqlx.DB) (err error) {
for i := 0; i < attempts; i++ {
err = db.PingContext(ctx)
if err == nil {
return nil
}
time.Sleep(sleep)
}
return err
}
// checkExistence checks if tables have been created yet, it is not concerned
// about the existence of the schema migration version (since migrations were
// added to existing dbs, we need to know whether the db exists without migrations