You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
2.9 KiB
109 lines
2.9 KiB
/**
|
|
* Unified backoff policy implementation
|
|
*/
|
|
|
|
import { BackoffPolicy } from './types';
|
|
import { DEFAULT_CONFIG } from './constants';
|
|
|
|
/**
|
|
* Calculate backoff delay with Retry-After + jittered exponential caps
|
|
*/
|
|
export function calculateBackoffDelay(
|
|
attempt: number,
|
|
policy: BackoffPolicy,
|
|
retryAfterMs?: number
|
|
): number {
|
|
let delay: number;
|
|
|
|
// Respect Retry-After header if present and enabled
|
|
if (policy.respectRetryAfter && retryAfterMs !== undefined) {
|
|
delay = Math.min(retryAfterMs, policy.retryAfterMaxMs || policy.maxDelayMs);
|
|
} else {
|
|
// Calculate base delay based on strategy
|
|
switch (policy.strategy) {
|
|
case 'exponential':
|
|
delay = policy.baseDelayMs * Math.pow(2, attempt - 1);
|
|
break;
|
|
case 'linear':
|
|
delay = policy.baseDelayMs * attempt;
|
|
break;
|
|
case 'fixed':
|
|
delay = policy.baseDelayMs;
|
|
break;
|
|
default:
|
|
delay = policy.baseDelayMs;
|
|
}
|
|
}
|
|
|
|
// Apply jitter if enabled
|
|
if (policy.jitterEnabled) {
|
|
const jitterRange = delay * policy.jitterFactor;
|
|
const jitter = (Math.random() - 0.5) * 2 * jitterRange;
|
|
delay = Math.max(0, delay + jitter);
|
|
}
|
|
|
|
// Cap at maximum delay
|
|
return Math.min(delay, policy.maxDelayMs);
|
|
}
|
|
|
|
/**
|
|
* Create default backoff policy
|
|
*/
|
|
export function createDefaultBackoffPolicy(): BackoffPolicy {
|
|
return {
|
|
maxAttempts: 3,
|
|
baseDelayMs: DEFAULT_CONFIG.baseDelayMs,
|
|
maxDelayMs: DEFAULT_CONFIG.maxDelayMs,
|
|
strategy: 'exponential',
|
|
jitterEnabled: true,
|
|
jitterFactor: DEFAULT_CONFIG.jitterFactor,
|
|
respectRetryAfter: DEFAULT_CONFIG.respectRetryAfter,
|
|
retryAfterMaxMs: DEFAULT_CONFIG.retryAfterMaxMs
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create backoff policy for rate limiting
|
|
*/
|
|
export function createRateLimitBackoffPolicy(retryAfterMs: number): BackoffPolicy {
|
|
return {
|
|
maxAttempts: 5,
|
|
baseDelayMs: retryAfterMs,
|
|
maxDelayMs: Math.max(retryAfterMs * 2, DEFAULT_CONFIG.maxDelayMs),
|
|
strategy: 'fixed',
|
|
jitterEnabled: true,
|
|
jitterFactor: 0.1, // ±10% jitter for rate limits
|
|
respectRetryAfter: true,
|
|
retryAfterMaxMs: retryAfterMs * 2
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create backoff policy for network errors
|
|
*/
|
|
export function createNetworkErrorBackoffPolicy(): BackoffPolicy {
|
|
return {
|
|
maxAttempts: 3,
|
|
baseDelayMs: DEFAULT_CONFIG.baseDelayMs,
|
|
maxDelayMs: DEFAULT_CONFIG.maxDelayMs,
|
|
strategy: 'exponential',
|
|
jitterEnabled: true,
|
|
jitterFactor: DEFAULT_CONFIG.jitterFactor,
|
|
respectRetryAfter: false
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create backoff policy for server errors (5xx)
|
|
*/
|
|
export function createServerErrorBackoffPolicy(): BackoffPolicy {
|
|
return {
|
|
maxAttempts: 3,
|
|
baseDelayMs: 2000, // Start with 2s for server errors
|
|
maxDelayMs: DEFAULT_CONFIG.maxDelayMs,
|
|
strategy: 'exponential',
|
|
jitterEnabled: true,
|
|
jitterFactor: 0.5, // ±50% jitter for server errors
|
|
respectRetryAfter: false
|
|
};
|
|
}
|
|
|