fix(rate-limit): make 429 fallback cooldown configurable

This commit is contained in:
gaoren002
2026-04-30 03:01:39 +00:00
parent 8ad099baa6
commit 4b904c887c
15 changed files with 520 additions and 12 deletions
+53 -10
View File
@@ -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 {