From efd14ac7dbea4fb28e44871a5bc2781954504235 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Mon, 27 May 2019 11:42:28 +0200 Subject: [PATCH] database: retry transaction if sqlite returns busy error (#17297) Adds an additional sqlite error code 5 (SQLITE_BUSY) to the transaction retry handler to add retries when sqlite returns database is locked error. More info: https://www.sqlite.org/rescode.html#busy Backports #17276 for v6.2.x --- pkg/services/sqlstore/sqlstore.go | 6 +++--- pkg/services/sqlstore/transactions.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 116c246cbe3..6ea6c4e3033 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -88,12 +88,12 @@ func (ss *SqlStore) inTransactionWithRetryCtx(ctx context.Context, callback dbTr err = callback(sess) - // special handling of database locked errors for sqlite, then we can retry 3 times + // special handling of database locked errors for sqlite, then we can retry 5 times if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 { - if sqlError.Code == sqlite3.ErrLocked { + if sqlError.Code == sqlite3.ErrLocked || sqlError.Code == sqlite3.ErrBusy { sess.Rollback() time.Sleep(time.Millisecond * time.Duration(10)) - sqlog.Info("Database table locked, sleeping then retrying", "retry", retry) + sqlog.Info("Database locked, sleeping then retrying", "error", err, "retry", retry) return ss.inTransactionWithRetryCtx(ctx, callback, retry+1) } } diff --git a/pkg/services/sqlstore/transactions.go b/pkg/services/sqlstore/transactions.go index edf29fffb8f..d3e22e3bdf5 100644 --- a/pkg/services/sqlstore/transactions.go +++ b/pkg/services/sqlstore/transactions.go @@ -25,12 +25,12 @@ func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx cont err = fn(withValue) - // special handling of database locked errors for sqlite, then we can retry 3 times + // special handling of database locked errors for sqlite, then we can retry 5 times if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 { - if sqlError.Code == sqlite3.ErrLocked { + if sqlError.Code == sqlite3.ErrLocked || sqlError.Code == sqlite3.ErrBusy { sess.Rollback() time.Sleep(time.Millisecond * time.Duration(10)) - ss.log.Info("Database table locked, sleeping then retrying", "retry", retry) + ss.log.Info("Database locked, sleeping then retrying", "error", err, "retry", retry) return ss.inTransactionWithRetry(ctx, fn, retry+1) } } @@ -69,12 +69,12 @@ func inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, err = callback(sess) - // special handling of database locked errors for sqlite, then we can retry 3 times + // special handling of database locked errors for sqlite, then we can retry 5 times if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 { - if sqlError.Code == sqlite3.ErrLocked { + if sqlError.Code == sqlite3.ErrLocked || sqlError.Code == sqlite3.ErrBusy { sess.Rollback() time.Sleep(time.Millisecond * time.Duration(10)) - sqlog.Info("Database table locked, sleeping then retrying", "retry", retry) + sqlog.Info("Database locked, sleeping then retrying", "error", err, "retry", retry) return inTransactionWithRetry(callback, retry+1) } }