|
|
|
@ -26,7 +26,7 @@ import ( |
|
|
|
|
|
|
|
|
|
_ "github.com/grafana/grafana/pkg/tsdb/mssql" |
|
|
|
|
_ "github.com/lib/pq" |
|
|
|
|
_ "github.com/mattn/go-sqlite3" |
|
|
|
|
sqlite3 "github.com/mattn/go-sqlite3" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
@ -56,6 +56,64 @@ type SqlStore struct { |
|
|
|
|
skipEnsureAdmin bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// NewSession returns a new DBSession
|
|
|
|
|
func (ss *SqlStore) NewSession() *DBSession { |
|
|
|
|
return &DBSession{Session: ss.engine.NewSession()} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// WithDbSession calls the callback with an session attached to the context.
|
|
|
|
|
func (ss *SqlStore) WithDbSession(ctx context.Context, callback dbTransactionFunc) error { |
|
|
|
|
sess, err := startSession(ctx, ss.engine, false) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return callback(sess) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// WithTransactionalDbSession calls the callback with an session within a transaction
|
|
|
|
|
func (ss *SqlStore) WithTransactionalDbSession(ctx context.Context, callback dbTransactionFunc) error { |
|
|
|
|
return ss.inTransactionWithRetryCtx(ctx, callback, 0) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (ss *SqlStore) inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error { |
|
|
|
|
sess, err := startSession(ctx, ss.engine, true) |
|
|
|
|
if err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
defer sess.Close() |
|
|
|
|
|
|
|
|
|
err = callback(sess) |
|
|
|
|
|
|
|
|
|
// special handling of database locked errors for sqlite, then we can retry 3 times
|
|
|
|
|
if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 { |
|
|
|
|
if sqlError.Code == sqlite3.ErrLocked { |
|
|
|
|
sess.Rollback() |
|
|
|
|
time.Sleep(time.Millisecond * time.Duration(10)) |
|
|
|
|
sqlog.Info("Database table locked, sleeping then retrying", "retry", retry) |
|
|
|
|
return ss.inTransactionWithRetryCtx(ctx, callback, retry+1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
sess.Rollback() |
|
|
|
|
return err |
|
|
|
|
} else if err = sess.Commit(); err != nil { |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if len(sess.events) > 0 { |
|
|
|
|
for _, e := range sess.events { |
|
|
|
|
if err = bus.Publish(e); err != nil { |
|
|
|
|
log.Error(3, "Failed to publish event after commit", err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (ss *SqlStore) Init() error { |
|
|
|
|
ss.log = log.New("sqlstore") |
|
|
|
|
ss.readConfig() |
|
|
|
|