Skip to content

Commit 3002a2d

Browse files
committed
feat: make retry backoff constants configurable
Several 429 retry and backoff thresholds were still hardcoded in the rate-limit backoff module, unlike other runtime retry controls that already respect plugin config and environment overrides. Expose those constants through plugin config/env getters and apply them at runtime so operators can tune dedup windows, reset windows, max backoff, and short-retry behavior without patching code.
1 parent 48e2438 commit 3002a2d

8 files changed

Lines changed: 458 additions & 54 deletions

File tree

index.ts

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ import {
6565
getFastSessionMaxInputItems,
6666
getFastSessionStrategy,
6767
getFetchTimeoutMs,
68+
getRateLimitDedupWindowMs,
69+
getRateLimitMaxBackoffMs,
70+
getRateLimitShortRetryThresholdMs,
71+
getRateLimitStateResetMs,
6872
getLiveAccountSync,
6973
getLiveAccountSyncDebounceMs,
7074
getLiveAccountSyncPollMs,
@@ -166,9 +170,10 @@ import {
166170
transformRequestForCodex,
167171
} from "./lib/request/fetch-helpers.js";
168172
import {
173+
configureRateLimitBackoff,
169174
getRateLimitBackoff,
170175
MAX_SHORT_RETRY_ATTEMPTS,
171-
RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS,
176+
getRateLimitShortRetryThresholdMs as getConfiguredRateLimitShortRetryThresholdMs,
172177
resetRateLimitBackoff,
173178
} from "./lib/request/rate-limit-backoff.js";
174179
import {
@@ -693,6 +698,13 @@ let sessionAffinityWriteVersion = 0;
693698
const fastSessionMaxInputItems =
694699
getFastSessionMaxInputItems(pluginConfig);
695700
const tokenRefreshSkewMs = getTokenRefreshSkewMs(pluginConfig);
701+
configureRateLimitBackoff({
702+
dedupWindowMs: getRateLimitDedupWindowMs(pluginConfig),
703+
stateResetMs: getRateLimitStateResetMs(pluginConfig),
704+
maxBackoffMs: getRateLimitMaxBackoffMs(pluginConfig),
705+
shortRetryThresholdMs:
706+
getRateLimitShortRetryThresholdMs(pluginConfig),
707+
});
696708
const rateLimitToastDebounceMs =
697709
getRateLimitToastDebounceMs(pluginConfig);
698710
const retryAllAccountsRateLimited =
@@ -1277,6 +1289,7 @@ let sessionAffinityWriteVersion = 0;
12771289
}
12781290

12791291
let sameAccountRetryCount = 0;
1292+
let shortRateLimitRetryCount = 0;
12801293
let successAccountForResponse = account;
12811294
let successEntitlementAccountKey = entitlementAccountKey;
12821295
while (true) {
@@ -1887,10 +1900,11 @@ let sessionAffinityWriteVersion = 0;
18871900
runtimeMetrics.rateLimitedResponses++;
18881901
const retryAfterMs =
18891902
rateLimit?.retryAfterMs ?? 60_000;
1890-
const { attempt, delayMs } = getRateLimitBackoff(
1903+
const { delayMs } = getRateLimitBackoff(
18911904
account.index,
18921905
quotaKey,
18931906
retryAfterMs,
1907+
account.accountId ?? account.email ?? null,
18941908
);
18951909
const cooldownMs = Math.max(
18961910
delayMs,
@@ -1901,30 +1915,35 @@ let sessionAffinityWriteVersion = 0;
19011915
cooldownMs,
19021916
);
19031917
const waitLabel = formatWaitTime(cooldownMs);
1918+
const shortRetryThresholdMs =
1919+
getConfiguredRateLimitShortRetryThresholdMs();
19041920

1905-
if (
1906-
cooldownMs <= RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS &&
1907-
attempt < MAX_SHORT_RETRY_ATTEMPTS
1908-
) {
1909-
if (
1910-
accountManager.shouldShowAccountToast(
1911-
account.index,
1912-
rateLimitToastDebounceMs,
1913-
)
1914-
) {
1915-
await showRuntimeToast(client,
1916-
`Rate limited. Retrying in ${waitLabel} (attempt ${attempt})...`,
1917-
"warning",
1918-
{ duration: toastDurationMs },
1919-
);
1920-
accountManager.markToastShown(account.index);
1921-
}
1921+
if (
1922+
cooldownMs <=
1923+
shortRetryThresholdMs &&
1924+
shortRateLimitRetryCount < MAX_SHORT_RETRY_ATTEMPTS
1925+
) {
1926+
shortRateLimitRetryCount += 1;
1927+
if (
1928+
accountManager.shouldShowAccountToast(
1929+
account.index,
1930+
rateLimitToastDebounceMs,
1931+
)
1932+
) {
1933+
await showRuntimeToast(
1934+
client,
1935+
`Rate limited. Retrying in ${waitLabel} (attempt ${shortRateLimitRetryCount})...`,
1936+
"warning",
1937+
{ duration: toastDurationMs },
1938+
);
1939+
accountManager.markToastShown(account.index);
1940+
}
19221941

1923-
await sleep(
1924-
addJitter(Math.max(MIN_BACKOFF_MS, cooldownMs), 0.2),
1925-
);
1926-
continue;
1927-
}
1942+
await sleep(
1943+
addJitter(Math.max(MIN_BACKOFF_MS, cooldownMs), 0.2),
1944+
);
1945+
continue;
1946+
}
19281947

19291948
accountManager.markRateLimitedWithReason(
19301949
account,

lib/config.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ export const DEFAULT_PLUGIN_CONFIG: PluginConfig = {
183183
parallelProbingMaxConcurrency: 2,
184184
emptyResponseMaxRetries: 2,
185185
emptyResponseRetryDelayMs: 1_000,
186+
rateLimitDedupWindowMs: 2_000,
187+
rateLimitStateResetMs: 120_000,
188+
rateLimitMaxBackoffMs: 60_000,
189+
rateLimitShortRetryThresholdMs: 5_000,
186190
pidOffsetEnabled: false,
187191
fetchTimeoutMs: 60_000,
188192
streamStallTimeoutMs: 45_000,
@@ -1076,6 +1080,44 @@ export function getEmptyResponseRetryDelayMs(
10761080
);
10771081
}
10781082

1083+
export function getRateLimitDedupWindowMs(pluginConfig: PluginConfig): number {
1084+
return resolveNumberSetting(
1085+
"CODEX_AUTH_RATE_LIMIT_DEDUP_WINDOW_MS",
1086+
pluginConfig.rateLimitDedupWindowMs,
1087+
DEFAULT_PLUGIN_CONFIG.rateLimitDedupWindowMs,
1088+
{ min: 0 },
1089+
);
1090+
}
1091+
1092+
export function getRateLimitStateResetMs(pluginConfig: PluginConfig): number {
1093+
return resolveNumberSetting(
1094+
"CODEX_AUTH_RATE_LIMIT_STATE_RESET_MS",
1095+
pluginConfig.rateLimitStateResetMs,
1096+
DEFAULT_PLUGIN_CONFIG.rateLimitStateResetMs,
1097+
{ min: 1_000 },
1098+
);
1099+
}
1100+
1101+
export function getRateLimitMaxBackoffMs(pluginConfig: PluginConfig): number {
1102+
return resolveNumberSetting(
1103+
"CODEX_AUTH_RATE_LIMIT_MAX_BACKOFF_MS",
1104+
pluginConfig.rateLimitMaxBackoffMs,
1105+
DEFAULT_PLUGIN_CONFIG.rateLimitMaxBackoffMs,
1106+
{ min: 1_000 },
1107+
);
1108+
}
1109+
1110+
export function getRateLimitShortRetryThresholdMs(
1111+
pluginConfig: PluginConfig,
1112+
): number {
1113+
return resolveNumberSetting(
1114+
"CODEX_AUTH_RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS",
1115+
pluginConfig.rateLimitShortRetryThresholdMs,
1116+
DEFAULT_PLUGIN_CONFIG.rateLimitShortRetryThresholdMs,
1117+
{ min: 0 },
1118+
);
1119+
}
1120+
10791121
export function getPidOffsetEnabled(pluginConfig: PluginConfig): boolean {
10801122
return resolveBooleanSetting(
10811123
"CODEX_AUTH_PID_OFFSET_ENABLED",
@@ -1678,6 +1720,26 @@ const CONFIG_EXPLAIN_ENTRIES: ConfigExplainMeta[] = [
16781720
envNames: ["CODEX_AUTH_EMPTY_RESPONSE_RETRY_DELAY_MS"],
16791721
getValue: getEmptyResponseRetryDelayMs,
16801722
},
1723+
{
1724+
key: "rateLimitDedupWindowMs",
1725+
envNames: ["CODEX_AUTH_RATE_LIMIT_DEDUP_WINDOW_MS"],
1726+
getValue: getRateLimitDedupWindowMs,
1727+
},
1728+
{
1729+
key: "rateLimitStateResetMs",
1730+
envNames: ["CODEX_AUTH_RATE_LIMIT_STATE_RESET_MS"],
1731+
getValue: getRateLimitStateResetMs,
1732+
},
1733+
{
1734+
key: "rateLimitMaxBackoffMs",
1735+
envNames: ["CODEX_AUTH_RATE_LIMIT_MAX_BACKOFF_MS"],
1736+
getValue: getRateLimitMaxBackoffMs,
1737+
},
1738+
{
1739+
key: "rateLimitShortRetryThresholdMs",
1740+
envNames: ["CODEX_AUTH_RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS"],
1741+
getValue: getRateLimitShortRetryThresholdMs,
1742+
},
16811743
{
16821744
key: "pidOffsetEnabled",
16831745
envNames: ["CODEX_AUTH_PID_OFFSET_ENABLED"],

0 commit comments

Comments
 (0)