fix(opencode): cap Retry-After hint at 24h for usage refresh backoff

This commit is contained in:
ASWJ
2026-10-06 23:55:46 +08:00
parent b8dece9000
commit aa3b049000
2 changed files with 38 additions and 1 deletions
+10 -1
View File
@@ -1000,8 +1000,17 @@ func decodeOpenCodeGoUsageSnapshot(extra map[string]any) *OpenCodeGoUsageSnapsho
// nextOpenCodeGoUsageDelay computes the not-before delay for the next refresh:
// interval * 2^min(failureCount-1, 6) capped at 24h, ±10% jitter capped at 5min,
// never below a Retry-After hint or one minute.
// never below a Retry-After hint or one minute. The hint is honored over the
// local backoff but clamped to the same 24h ceiling: it arrives over a network
// path we do not control (upstream CDN, account proxy), and an unbounded value
// would freeze a group's auto refresh indefinitely.
func nextOpenCodeGoUsageDelay(intervalMinutes, failureCount int, retryAfterDuration time.Duration) time.Duration {
if retryAfterDuration < 0 {
retryAfterDuration = 0
}
if retryAfterDuration > opencodeGoUsageMaxDelay {
retryAfterDuration = opencodeGoUsageMaxDelay
}
minimumDelay := retryAfterDuration
base := time.Duration(intervalMinutes) * time.Minute
if base < opencodeGoUsageMinIntervalMinutes*time.Minute {
@@ -403,11 +403,39 @@ func TestNextOpenCodeGoUsageDelayBackoff(t *testing.T) {
delay = nextOpenCodeGoUsageDelay(15, 0, 2*time.Hour)
require.GreaterOrEqual(t, delay, 2*time.Hour)
// a hint beyond the 24h ceiling is clamped, never adopted verbatim
delay = nextOpenCodeGoUsageDelay(15, 0, 48*time.Hour)
require.GreaterOrEqual(t, delay, 23*time.Hour+55*time.Minute)
require.LessOrEqual(t, delay, 24*time.Hour+5*time.Minute)
// floor of one minute
delay = nextOpenCodeGoUsageDelay(5, 0, 0)
require.GreaterOrEqual(t, delay, time.Minute)
}
func TestOpenCodeGoUsageRefreshClampsHugeRetryAfter(t *testing.T) {
now := time.Now().UTC()
account := openCodeGoUsageAccount(7)
repo := &openCodeGoUsageTestRepo{accounts: map[int64]*Account{7: account}}
stub := &openCodeGoUsageHTTPStub{
status: http.StatusTooManyRequests,
header: http.Header{"Retry-After": []string{"99999999"}},
}
svc := newOpenCodeGoUsageTestService(t, repo, stub, &upstreamBillingProbeSettingRepo{})
svc.now = func() time.Time { return now }
state, err := svc.Refresh(context.Background(), 7)
require.NoError(t, err)
require.Equal(t, OpenCodeGoUsageStatusFailed, state.Snapshot.Status)
require.Equal(t, http.StatusTooManyRequests, state.Snapshot.HTTPStatus)
require.Equal(t, "http_error", state.Snapshot.LastError)
maxExpected := now.Add(24*time.Hour + 5*time.Minute)
minExpected := now.Add(23*time.Hour + 55*time.Minute)
require.True(t, state.Snapshot.NextRefreshAt.Before(maxExpected.Add(time.Second)))
require.True(t, state.Snapshot.NextRefreshAt.After(minExpected.Add(-time.Second)))
}
func TestOpenCodeGoUsageManualRefreshThrottle(t *testing.T) {
now := time.Now().UTC()
account := openCodeGoUsageAccount(7)