fix: enhance retry logic and configuration for HTTP status codes

This commit is contained in:
decolua
2026-04-24 10:07:08 +07:00
parent 65f11a603e
commit 0aff9a502c
2 changed files with 20 additions and 12 deletions

View File

@@ -45,8 +45,9 @@ export const RETRY_CONFIG = {
// Backward compat: if value is a number, treated as attempts with RETRY_CONFIG.delayMs
export const DEFAULT_RETRY_CONFIG = {
429: { attempts: 0, delayMs: 0 },
502: { attempts: 1, delayMs: 3000 },
503: { attempts: 1, delayMs: 2000 }
502: { attempts: 3, delayMs: 3000 },
503: { attempts: 3, delayMs: 2000 },
504: { attempts: 2, delayMs: 3000 }
};
// Normalize a retry entry to { attempts, delayMs }

View File

@@ -108,6 +108,16 @@ export class BaseExecutor {
// Merge default retry config with provider-specific config
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
// Schedule retry via retryConfig[statusKey]. Returns true when caller should `urlIndex--; continue`
const tryRetry = async (urlIndex, statusKey, reason) => {
const { attempts, delayMs } = resolveRetryEntry(retryConfig[statusKey]);
if (attempts <= 0 || retryAttemptsByUrl[urlIndex] >= attempts) return false;
retryAttemptsByUrl[urlIndex]++;
log?.debug?.("RETRY", `${reason} retry ${retryAttemptsByUrl[urlIndex]}/${attempts} after ${delayMs / 1000}s`);
await new Promise(resolve => setTimeout(resolve, delayMs));
return true;
};
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
const url = this.buildUrl(model, stream, urlIndex, credentials);
const transformedBody = this.transformRequest(model, body, stream, credentials);
@@ -123,15 +133,7 @@ export class BaseExecutor {
signal
}, proxyOptions);
// Retry based on status code config
const { attempts: maxRetries, delayMs } = resolveRetryEntry(retryConfig[response.status]);
if (maxRetries > 0 && retryAttemptsByUrl[urlIndex] < maxRetries) {
retryAttemptsByUrl[urlIndex]++;
log?.debug?.("RETRY", `${response.status} retry ${retryAttemptsByUrl[urlIndex]}/${maxRetries} after ${delayMs / 1000}s`);
await new Promise(resolve => setTimeout(resolve, delayMs));
urlIndex--;
continue;
}
if (await tryRetry(urlIndex, response.status, `status ${response.status}`)) { urlIndex--; continue; }
if (this.shouldRetry(response.status, urlIndex)) {
log?.debug?.("RETRY", `${response.status} on ${url}, trying fallback ${urlIndex + 1}`);
@@ -142,6 +144,11 @@ export class BaseExecutor {
return { response, url, headers, transformedBody };
} catch (error) {
lastError = error;
if (error.name === "AbortError") throw error;
// Map network/fetch exceptions to 502 retry config
if (await tryRetry(urlIndex, HTTP_STATUS.BAD_GATEWAY, `network "${error.message}"`)) { urlIndex--; continue; }
if (urlIndex + 1 < fallbackCount) {
log?.debug?.("RETRY", `Error on ${url}, trying fallback ${urlIndex + 1}`);
continue;