SQLStore: Optionally retry queries if sqlite returns database is locked (#56096)
* SQLStore: Retry queries if sqlite returns database is locked * Configurable retries * Apply suggestions from code review Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
co-authored by
Christopher Moyer
parent
920d2aa599
commit
46fb4081ba
@@ -17,7 +17,7 @@ var tsclogger = log.New("sqlstore.transactions")
|
||||
|
||||
// WithTransactionalDbSession calls the callback with a session within a transaction.
|
||||
func (ss *SQLStore) WithTransactionalDbSession(ctx context.Context, callback DBTransactionFunc) error {
|
||||
return inTransactionWithRetryCtx(ctx, ss.engine, ss.bus, callback, 0)
|
||||
return ss.inTransactionWithRetryCtx(ctx, ss.engine, ss.bus, callback, 0)
|
||||
}
|
||||
|
||||
// InTransaction starts a transaction and calls the fn
|
||||
@@ -27,13 +27,13 @@ func (ss *SQLStore) InTransaction(ctx context.Context, fn func(ctx context.Conte
|
||||
}
|
||||
|
||||
func (ss *SQLStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
|
||||
return inTransactionWithRetryCtx(ctx, ss.engine, ss.bus, func(sess *DBSession) error {
|
||||
return ss.inTransactionWithRetryCtx(ctx, ss.engine, ss.bus, func(sess *DBSession) error {
|
||||
withValue := context.WithValue(ctx, ContextSessionKey{}, sess)
|
||||
return fn(withValue)
|
||||
}, retry)
|
||||
}
|
||||
|
||||
func inTransactionWithRetryCtx(ctx context.Context, engine *xorm.Engine, bus bus.Bus, callback DBTransactionFunc, retry int) error {
|
||||
func (ss *SQLStore) inTransactionWithRetryCtx(ctx context.Context, engine *xorm.Engine, bus bus.Bus, callback DBTransactionFunc, retry int) error {
|
||||
sess, isNew, err := startSessionOrUseExisting(ctx, engine, true)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,14 +60,14 @@ func inTransactionWithRetryCtx(ctx context.Context, engine *xorm.Engine, bus bus
|
||||
|
||||
// special handling of database locked errors for sqlite, then we can retry 5 times
|
||||
var sqlError sqlite3.Error
|
||||
if errors.As(err, &sqlError) && retry < 5 && (sqlError.Code == sqlite3.ErrLocked || sqlError.Code == sqlite3.ErrBusy) {
|
||||
if errors.As(err, &sqlError) && retry < ss.dbCfg.TransactionRetries && (sqlError.Code == sqlite3.ErrLocked || sqlError.Code == sqlite3.ErrBusy) {
|
||||
if rollErr := sess.Rollback(); rollErr != nil {
|
||||
return fmt.Errorf("rolling back transaction due to error failed: %s: %w", rollErr, err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * time.Duration(10))
|
||||
ctxLogger.Info("Database locked, sleeping then retrying", "error", err, "retry", retry)
|
||||
return inTransactionWithRetryCtx(ctx, engine, bus, callback, retry+1)
|
||||
ctxLogger.Info("Database locked, sleeping then retrying", "error", err, "retry", retry, "code", sqlError.Code)
|
||||
return ss.inTransactionWithRetryCtx(ctx, engine, bus, callback, retry+1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user