[INT-API-005] Add retry logic for 429 rate limit responses
This commit is contained in:
parent
6bb5cccdfa
commit
d4733e8674
2 changed files with 31 additions and 18 deletions
|
|
@ -560,7 +560,8 @@
|
|||
"description": "Détecter les réponses 429 et retry avec backoff basé sur Retry-After header.",
|
||||
"priority": "P2",
|
||||
"priority_rank": 16,
|
||||
"status": "todo",
|
||||
"status": "completed",
|
||||
"completed_at": "2025-01-27T15:30:00Z",
|
||||
"estimated_hours": 1.5,
|
||||
"side": "frontend_only",
|
||||
"files_to_modify": [
|
||||
|
|
@ -1097,13 +1098,13 @@
|
|||
},
|
||||
"progress_tracking": {
|
||||
"total_tasks": 32,
|
||||
"completed": 15,
|
||||
"completed": 16,
|
||||
"in_progress": 0,
|
||||
"todo": 17,
|
||||
"todo": 16,
|
||||
"blocked": 0,
|
||||
"completion_percentage": 47,
|
||||
"last_updated": "2025-01-27T15:15:00Z",
|
||||
"completion_percentage": 50,
|
||||
"last_updated": "2025-01-27T15:30:00Z",
|
||||
"estimated_completion_date": null,
|
||||
"estimated_hours_remaining": 26.5
|
||||
"estimated_hours_remaining": 25
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -607,20 +607,25 @@ apiClient.interceptors.response.use(
|
|||
}
|
||||
}
|
||||
|
||||
// Unified retry logic for all retryable errors
|
||||
// INT-API-005: Unified retry logic for all retryable errors
|
||||
const status = error.response?.status;
|
||||
const retryCount = (originalRequest as any)?._retryCount || 0;
|
||||
const maxRetries = DEFAULT_RETRY_CONFIG.maxRetries;
|
||||
|
||||
// INT-API-005: For 429 rate limit errors, use a specific max retries limit (3)
|
||||
const isRateLimitError = status === 429;
|
||||
const effectiveMaxRetries = isRateLimitError ? 3 : maxRetries; // Define here so it's accessible in both if blocks
|
||||
|
||||
// Check if error is retryable
|
||||
if (isRetryableError(error, DEFAULT_RETRY_CONFIG) && originalRequest && retryCount < maxRetries) {
|
||||
if (isRetryableError(error, DEFAULT_RETRY_CONFIG) && originalRequest && retryCount < effectiveMaxRetries) {
|
||||
// For non-idempotent methods (POST, PUT, DELETE, PATCH), only retry on specific errors
|
||||
const method = originalRequest.method?.toUpperCase();
|
||||
const isIdempotent = isIdempotentMethod(method);
|
||||
|
||||
// For non-idempotent methods, only retry on network errors or 5xx errors (not 429)
|
||||
if (!isIdempotent && status && status !== 500 && status !== 502 && status !== 503 && status !== 504) {
|
||||
// Don't retry non-idempotent methods on client errors (except 5xx)
|
||||
// INT-API-005: Allow retry for 429 rate limit errors even for non-idempotent methods
|
||||
// For non-idempotent methods, only retry on network errors, 5xx errors, or 429 rate limit
|
||||
if (!isIdempotent && status && status !== 429 && status !== 500 && status !== 502 && status !== 503 && status !== 504) {
|
||||
// Don't retry non-idempotent methods on client errors (except 429 and 5xx)
|
||||
const apiError = parseApiError(error);
|
||||
return Promise.reject(apiError);
|
||||
}
|
||||
|
|
@ -628,40 +633,46 @@ apiClient.interceptors.response.use(
|
|||
// Mark that we're retrying this request
|
||||
(originalRequest as any)._retryCount = retryCount + 1;
|
||||
|
||||
// Calculate delay (respect Retry-After header if present, otherwise exponential backoff with jitter)
|
||||
// INT-API-005: Calculate delay (respect Retry-After header if present for 429, otherwise exponential backoff with jitter)
|
||||
// For 429 rate limit errors, getRetryDelay will use Retry-After header if present
|
||||
const delay = getRetryDelay(error, retryCount, DEFAULT_RETRY_CONFIG.baseDelay, DEFAULT_RETRY_CONFIG.maxDelay);
|
||||
|
||||
// Log retry attempt with request_id if available
|
||||
const apiError = parseApiError(error);
|
||||
const errorType = status ? `HTTP ${status}` : error.code || 'Network Error';
|
||||
|
||||
// INT-API-005: Log retry attempt with appropriate max retries (3 for 429, default for others)
|
||||
if (apiError.request_id) {
|
||||
console.warn(
|
||||
`[API Retry] ${errorType} error, retrying (${retryCount + 1}/${maxRetries}) - Request ID: ${apiError.request_id}`,
|
||||
`[API Retry] ${errorType} error, retrying (${retryCount + 1}/${effectiveMaxRetries}) - Request ID: ${apiError.request_id}`,
|
||||
{
|
||||
status: status || 'N/A',
|
||||
error_code: error.code || 'N/A',
|
||||
retry_count: retryCount + 1,
|
||||
max_retries: maxRetries,
|
||||
max_retries: effectiveMaxRetries,
|
||||
delay_ms: Math.round(delay),
|
||||
request_id: apiError.request_id,
|
||||
url: originalRequest?.url,
|
||||
method: originalRequest?.method,
|
||||
is_idempotent: isIdempotent,
|
||||
is_rate_limit: isRateLimitError,
|
||||
retry_after_header: error.response?.headers['retry-after'] || error.response?.headers['Retry-After'] || 'N/A',
|
||||
},
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`[API Retry] ${errorType} error, retrying (${retryCount + 1}/${maxRetries})`,
|
||||
`[API Retry] ${errorType} error, retrying (${retryCount + 1}/${effectiveMaxRetries})`,
|
||||
{
|
||||
status: status || 'N/A',
|
||||
error_code: error.code || 'N/A',
|
||||
retry_count: retryCount + 1,
|
||||
max_retries: maxRetries,
|
||||
max_retries: effectiveMaxRetries,
|
||||
delay_ms: Math.round(delay),
|
||||
url: originalRequest?.url,
|
||||
method: originalRequest?.method,
|
||||
is_idempotent: isIdempotent,
|
||||
is_rate_limit: isRateLimitError,
|
||||
retry_after_header: error.response?.headers['retry-after'] || error.response?.headers['Retry-After'] || 'N/A',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -673,8 +684,9 @@ apiClient.interceptors.response.use(
|
|||
});
|
||||
}
|
||||
|
||||
// If already retried maxRetries times or error is not retryable, reject immediately
|
||||
if (retryCount >= maxRetries) {
|
||||
// INT-API-005: If already retried effectiveMaxRetries times or error is not retryable, reject immediately
|
||||
// Reuse the same effectiveMaxRetries calculation from above
|
||||
if (retryCount >= effectiveMaxRetries) {
|
||||
const apiError = parseApiError(error);
|
||||
const errorType = status ? `HTTP ${status}` : error.code || 'Network Error';
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue