fix(rate-limit): make 429 fallback cooldown configurable
This commit is contained in:
@@ -55,6 +55,11 @@ type geminiUsageTotalsBatchProvider interface {
|
||||
|
||||
const geminiPrecheckCacheTTL = time.Minute
|
||||
|
||||
const (
|
||||
defaultRateLimit429CooldownSeconds = 5
|
||||
maxRateLimit429CooldownSeconds = 7200
|
||||
)
|
||||
|
||||
const (
|
||||
openAI403CooldownMinutesDefault = 10
|
||||
openAI403DisableThreshold = 3
|
||||
@@ -891,12 +896,8 @@ func (s *RateLimitService) handle429(ctx context.Context, account *Account, head
|
||||
return
|
||||
}
|
||||
|
||||
// 其他平台:没有重置时间,使用默认5分钟
|
||||
resetAt := time.Now().Add(5 * time.Minute)
|
||||
slog.Warn("rate_limit_no_reset_time", "account_id", account.ID, "platform", account.Platform, "using_default", "5m")
|
||||
if err := s.accountRepo.SetRateLimited(ctx, account.ID, resetAt); err != nil {
|
||||
slog.Warn("rate_limit_set_failed", "account_id", account.ID, "error", err)
|
||||
}
|
||||
// 其他平台:没有重置时间,使用可配置的秒级默认回避,避免误伤长时间不可调度。
|
||||
s.apply429FallbackRateLimit(ctx, account, "no_reset_time")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -904,10 +905,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *Account, head
|
||||
ts, err := strconv.ParseInt(resetTimestamp, 10, 64)
|
||||
if err != nil {
|
||||
slog.Warn("rate_limit_reset_parse_failed", "reset_timestamp", resetTimestamp, "error", err)
|
||||
resetAt := time.Now().Add(5 * time.Minute)
|
||||
if err := s.accountRepo.SetRateLimited(ctx, account.ID, resetAt); err != nil {
|
||||
slog.Warn("rate_limit_set_failed", "account_id", account.ID, "error", err)
|
||||
}
|
||||
s.apply429FallbackRateLimit(ctx, account, "reset_parse_failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -929,6 +927,51 @@ func (s *RateLimitService) handle429(ctx context.Context, account *Account, head
|
||||
slog.Info("account_rate_limited", "account_id", account.ID, "reset_at", resetAt)
|
||||
}
|
||||
|
||||
func (s *RateLimitService) apply429FallbackRateLimit(ctx context.Context, account *Account, reason string) {
|
||||
cooldown, enabled := s.get429FallbackCooldown(ctx, account)
|
||||
if !enabled {
|
||||
slog.Info("rate_limit_429_fallback_ignored", "account_id", account.ID, "platform", account.Platform, "reason", reason)
|
||||
return
|
||||
}
|
||||
|
||||
resetAt := time.Now().Add(cooldown)
|
||||
slog.Warn("rate_limit_429_fallback_used", "account_id", account.ID, "platform", account.Platform, "reason", reason, "using_default", cooldown.String())
|
||||
if err := s.accountRepo.SetRateLimited(ctx, account.ID, resetAt); err != nil {
|
||||
slog.Warn("rate_limit_set_failed", "account_id", account.ID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RateLimitService) get429FallbackCooldown(ctx context.Context, account *Account) (time.Duration, bool) {
|
||||
if s.settingService != nil {
|
||||
settings, err := s.settingService.GetRateLimit429CooldownSettings(ctx)
|
||||
if err == nil && settings != nil {
|
||||
if !settings.Enabled {
|
||||
return 0, false
|
||||
}
|
||||
seconds := clampRateLimit429CooldownSeconds(settings.CooldownSeconds)
|
||||
return time.Duration(seconds) * time.Second, true
|
||||
}
|
||||
slog.Warn("rate_limit_429_settings_read_failed", "account_id", account.ID, "error", err)
|
||||
}
|
||||
|
||||
seconds := defaultRateLimit429CooldownSeconds
|
||||
if s.cfg != nil && s.cfg.RateLimit.RateLimit429CooldownSeconds > 0 {
|
||||
seconds = s.cfg.RateLimit.RateLimit429CooldownSeconds
|
||||
}
|
||||
seconds = clampRateLimit429CooldownSeconds(seconds)
|
||||
return time.Duration(seconds) * time.Second, true
|
||||
}
|
||||
|
||||
func clampRateLimit429CooldownSeconds(seconds int) int {
|
||||
if seconds < 1 {
|
||||
return 1
|
||||
}
|
||||
if seconds > maxRateLimit429CooldownSeconds {
|
||||
return maxRateLimit429CooldownSeconds
|
||||
}
|
||||
return seconds
|
||||
}
|
||||
|
||||
// calculateOpenAI429ResetTime 从 OpenAI 429 响应头计算正确的重置时间
|
||||
// 返回 nil 表示无法从响应头中确定重置时间
|
||||
func calculateOpenAI429ResetTime(headers http.Header) *time.Time {
|
||||
|
||||
Reference in New Issue
Block a user