fix: sqlite测试并发优化 (#112)
This commit is contained in:
@@ -7,7 +7,9 @@ import (
|
|||||||
app_errors "gpt-load/internal/errors"
|
app_errors "gpt-load/internal/errors"
|
||||||
"gpt-load/internal/models"
|
"gpt-load/internal/models"
|
||||||
"gpt-load/internal/store"
|
"gpt-load/internal/store"
|
||||||
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -88,6 +90,33 @@ func (p *KeyProvider) UpdateStatus(apiKey *models.APIKey, group *models.Group, i
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// executeTransactionWithRetry wraps a database transaction with a retry mechanism.
|
||||||
|
func (p *KeyProvider) executeTransactionWithRetry(operation func(tx *gorm.DB) error) error {
|
||||||
|
const maxRetries = 3
|
||||||
|
const baseDelay = 50 * time.Millisecond
|
||||||
|
const maxJitter = 150 * time.Millisecond
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := range maxRetries {
|
||||||
|
err = p.db.Transaction(operation)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(err.Error(), "database is locked") {
|
||||||
|
jitter := time.Duration(rand.Intn(int(maxJitter)))
|
||||||
|
totalDelay := baseDelay + jitter
|
||||||
|
logrus.Debugf("Database is locked, retrying in %v... (attempt %d/%d)", totalDelay, i+1, maxRetries)
|
||||||
|
time.Sleep(totalDelay)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (p *KeyProvider) handleSuccess(keyID uint, keyHashKey, activeKeysListKey string) error {
|
func (p *KeyProvider) handleSuccess(keyID uint, keyHashKey, activeKeysListKey string) error {
|
||||||
keyDetails, err := p.store.HGetAll(keyHashKey)
|
keyDetails, err := p.store.HGetAll(keyHashKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -101,7 +130,7 @@ func (p *KeyProvider) handleSuccess(keyID uint, keyHashKey, activeKeysListKey st
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.db.Transaction(func(tx *gorm.DB) error {
|
return p.executeTransactionWithRetry(func(tx *gorm.DB) error {
|
||||||
var key models.APIKey
|
var key models.APIKey
|
||||||
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&key, keyID).Error; err != nil {
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&key, keyID).Error; err != nil {
|
||||||
return fmt.Errorf("failed to lock key %d for update: %w", keyID, err)
|
return fmt.Errorf("failed to lock key %d for update: %w", keyID, err)
|
||||||
@@ -149,7 +178,7 @@ func (p *KeyProvider) handleFailure(apiKey *models.APIKey, group *models.Group,
|
|||||||
// 获取该分组的有效配置
|
// 获取该分组的有效配置
|
||||||
blacklistThreshold := group.EffectiveConfig.BlacklistThreshold
|
blacklistThreshold := group.EffectiveConfig.BlacklistThreshold
|
||||||
|
|
||||||
return p.db.Transaction(func(tx *gorm.DB) error {
|
return p.executeTransactionWithRetry(func(tx *gorm.DB) error {
|
||||||
var key models.APIKey
|
var key models.APIKey
|
||||||
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&key, apiKey.ID).Error; err != nil {
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&key, apiKey.ID).Error; err != nil {
|
||||||
return fmt.Errorf("failed to lock key %d for update: %w", apiKey.ID, err)
|
return fmt.Errorf("failed to lock key %d for update: %w", apiKey.ID, err)
|
||||||
|
@@ -35,7 +35,7 @@ type SystemSettings struct {
|
|||||||
MaxRetries int `json:"max_retries" default:"3" name:"最大重试次数" category:"密钥配置" desc:"单个请求使用不同 Key 的最大重试次数,0为不重试。" validate:"min=0"`
|
MaxRetries int `json:"max_retries" default:"3" name:"最大重试次数" category:"密钥配置" desc:"单个请求使用不同 Key 的最大重试次数,0为不重试。" validate:"min=0"`
|
||||||
BlacklistThreshold int `json:"blacklist_threshold" default:"3" name:"黑名单阈值" category:"密钥配置" desc:"一个 Key 连续失败多少次后进入黑名单,0为不拉黑。" validate:"min=0"`
|
BlacklistThreshold int `json:"blacklist_threshold" default:"3" name:"黑名单阈值" category:"密钥配置" desc:"一个 Key 连续失败多少次后进入黑名单,0为不拉黑。" validate:"min=0"`
|
||||||
KeyValidationIntervalMinutes int `json:"key_validation_interval_minutes" default:"60" name:"密钥验证间隔(分钟)" category:"密钥配置" desc:"后台验证密钥的默认间隔(分钟)。" validate:"min=30"`
|
KeyValidationIntervalMinutes int `json:"key_validation_interval_minutes" default:"60" name:"密钥验证间隔(分钟)" category:"密钥配置" desc:"后台验证密钥的默认间隔(分钟)。" validate:"min=30"`
|
||||||
KeyValidationConcurrency int `json:"key_validation_concurrency" default:"10" name:"密钥验证并发数" category:"密钥配置" desc:"后台定时验证无效 Key 时的并发数。" validate:"min=1"`
|
KeyValidationConcurrency int `json:"key_validation_concurrency" default:"10" name:"密钥验证并发数" category:"密钥配置" desc:"后台定时验证无效 Key 时的并发数,如果使用SQLite或者运行环境性能不佳,请尽量保证20以下,避免过高的并发导致数据不一致问题。" validate:"min=1"`
|
||||||
KeyValidationTimeoutSeconds int `json:"key_validation_timeout_seconds" default:"20" name:"密钥验证超时(秒)" category:"密钥配置" desc:"后台定时验证单个 Key 时的 API 请求超时时间(秒)。" validate:"min=5"`
|
KeyValidationTimeoutSeconds int `json:"key_validation_timeout_seconds" default:"20" name:"密钥验证超时(秒)" category:"密钥配置" desc:"后台定时验证单个 Key 时的 API 请求超时时间(秒)。" validate:"min=5"`
|
||||||
|
|
||||||
// For cache
|
// For cache
|
||||||
|
Reference in New Issue
Block a user