fix: implement retry logic for upserting settings to handle transient database errors

This commit is contained in:
GitHub Actions
2026-02-13 07:09:35 +00:00
parent e1b648acb1
commit 8616c52da0
@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/gin-gonic/gin"
@@ -239,7 +240,7 @@ func (h *EmergencyHandler) disableAllSecurityModules() ([]string, error) {
Type: "bool",
}
if err := h.db.Where(models.Setting{Key: key}).Assign(setting).FirstOrCreate(&setting).Error; err != nil {
if err := h.upsertSettingWithRetry(&setting); err != nil {
return disabledModules, fmt.Errorf("failed to disable %s: %w", key, err)
}
disabledModules = append(disabledModules, key)
@@ -252,7 +253,7 @@ func (h *EmergencyHandler) disableAllSecurityModules() ([]string, error) {
Category: "security",
Type: "string",
}
if err := h.db.Where(models.Setting{Key: adminWhitelistSetting.Key}).Assign(adminWhitelistSetting).FirstOrCreate(&adminWhitelistSetting).Error; err != nil {
if err := h.upsertSettingWithRetry(&adminWhitelistSetting); err != nil {
return disabledModules, fmt.Errorf("failed to clear admin whitelist: %w", err)
}
@@ -274,6 +275,28 @@ func (h *EmergencyHandler) disableAllSecurityModules() ([]string, error) {
return disabledModules, nil
}
func (h *EmergencyHandler) upsertSettingWithRetry(setting *models.Setting) error {
const maxAttempts = 5
for attempt := 1; attempt <= maxAttempts; attempt++ {
err := h.db.Where(models.Setting{Key: setting.Key}).Assign(*setting).FirstOrCreate(setting).Error
if err == nil {
return nil
}
errMsg := strings.ToLower(err.Error())
isTransientLock := strings.Contains(errMsg, "database is locked") || strings.Contains(errMsg, "database table is locked") || strings.Contains(errMsg, "busy")
if isTransientLock && attempt < maxAttempts {
time.Sleep(time.Duration(attempt) * 10 * time.Millisecond)
continue
}
return err
}
return nil
}
// logAudit logs an emergency action to the security audit trail
func (h *EmergencyHandler) logAudit(actor, action, details string) {
if h.securityService == nil {