| package util |
|
|
| import ( |
| "os" |
| "strconv" |
| "time" |
| ) |
|
|
| |
| var ( |
| |
| AuthErrorInitialCooldown = 5 * time.Minute |
|
|
| |
| TimeoutErrorCooldown = time.Minute |
|
|
| |
| ServerErrorInitialCooldown = 2 * time.Minute |
|
|
| |
| RateLimitErrorCooldown = time.Minute |
|
|
| |
| MaxCooldownDuration = 30 * time.Minute |
|
|
| |
| MinCooldownDuration = 10 * time.Second |
| ) |
|
|
| func init() { |
| applyCooldownEnvOverrides(os.Getenv) |
| } |
|
|
| func envSecondsFrom(getenv func(string) string, key string) time.Duration { |
| s := getenv(key) |
| if s == "" { |
| return 0 |
| } |
| v, err := strconv.ParseInt(s, 10, 64) |
| if err != nil || v <= 0 { |
| return 0 |
| } |
| return time.Duration(v) * time.Second |
| } |
|
|
| func applyCooldownEnvOverrides(getenv func(string) string) { |
| |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_AUTH_SEC"); v > 0 { |
| AuthErrorInitialCooldown = v |
| } |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_TIMEOUT_SEC"); v > 0 { |
| TimeoutErrorCooldown = v |
| } |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_SERVER_SEC"); v > 0 { |
| ServerErrorInitialCooldown = v |
| } |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_RATE_LIMIT_SEC"); v > 0 { |
| RateLimitErrorCooldown = v |
| } |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_MAX_SEC"); v > 0 { |
| MaxCooldownDuration = v |
| } |
| if v := envSecondsFrom(getenv, "CCLOAD_COOLDOWN_MIN_SEC"); v > 0 { |
| MinCooldownDuration = v |
| } |
| } |
|
|
| |
| func CalculateBackoffDuration(prevMs int64, until time.Time, now time.Time, statusCode *int) time.Duration { |
| prev := time.Duration(prevMs) * time.Millisecond |
|
|
| |
| if prev <= 0 { |
| if !until.IsZero() && until.After(now) { |
| prev = until.Sub(now) |
| } else { |
| |
| return getInitialCooldown(statusCode) |
| } |
| } |
|
|
| |
| next := min(max(prev*2, MinCooldownDuration), MaxCooldownDuration) |
| return next |
| } |
|
|
| |
| func getInitialCooldown(statusCode *int) time.Duration { |
| if statusCode == nil { |
| return RateLimitErrorCooldown |
| } |
| code := *statusCode |
| switch { |
| case code == 401 || code == 402 || code == 403: |
| return AuthErrorInitialCooldown |
| case code == StatusFirstByteTimeout || code == StatusSSEError: |
| return TimeoutErrorCooldown |
| case code >= 500: |
| return ServerErrorInitialCooldown |
| default: |
| return RateLimitErrorCooldown |
| } |
| } |
|
|
| |
| func CalculateCooldownDuration(until time.Time, now time.Time) int64 { |
| if until.IsZero() || !until.After(now) { |
| return 0 |
| } |
| return int64(until.Sub(now) / time.Millisecond) |
| } |
|
|