api: harden delete retry classification and backoff
This commit is contained in:
@@ -97,9 +97,15 @@ export const delete_object = async function delete_object({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= retry_count; attempt++) {
|
for (let attempt = 1; attempt <= retry_count; attempt++) {
|
||||||
|
// Keep timeout handle at attempt scope so catch can always clear it.
|
||||||
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => {
|
// AbortError alone is ambiguous. Track helper-timeout aborts so
|
||||||
|
// caller/navigation aborts can still fail fast with no retry.
|
||||||
|
let did_timeout_abort = false;
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
did_timeout_abort = true;
|
||||||
console.error(
|
console.error(
|
||||||
`API DELETE request timed out after ${timeout}ms.`
|
`API DELETE request timed out after ${timeout}ms.`
|
||||||
);
|
);
|
||||||
@@ -120,12 +126,48 @@ export const delete_object = async function delete_object({
|
|||||||
url.toString(),
|
url.toString(),
|
||||||
fetchOptions
|
fetchOptions
|
||||||
).catch(function (error: any) {
|
).catch(function (error: any) {
|
||||||
|
if (
|
||||||
|
error?.name === 'AbortError' ||
|
||||||
|
error?.name === 'TypeError' ||
|
||||||
|
error?.message?.includes('aborted')
|
||||||
|
) {
|
||||||
|
if (log_lvl > 1) {
|
||||||
|
console.log(
|
||||||
|
'API DELETE: Request aborted or browser-terminated.',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'API DELETE Object *fetch* request was aborted or failed in an unexpected way.',
|
'API DELETE Object *fetch* request was aborted or failed in an unexpected way.',
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
|
return error;
|
||||||
});
|
});
|
||||||
clearTimeout(timeoutId);
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
// Error object was returned from fetch catch block; decide retry class.
|
||||||
|
if (
|
||||||
|
response instanceof Error ||
|
||||||
|
(response &&
|
||||||
|
(response.name === 'AbortError' ||
|
||||||
|
response.name === 'TypeError'))
|
||||||
|
) {
|
||||||
|
if (response.name === 'AbortError') {
|
||||||
|
if (did_timeout_abort) {
|
||||||
|
throw new Error(
|
||||||
|
`Timeout abort (attempt ${attempt}/${retry_count}) after ${timeout}ms`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Network error (attempt ${attempt}): ${response.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -151,7 +193,13 @@ export const delete_object = async function delete_object({
|
|||||||
errorBody
|
errorBody
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status >= 400 && response.status < 404) {
|
// Fail fast on client/auth/validation failures.
|
||||||
|
if (
|
||||||
|
response.status === 400 ||
|
||||||
|
response.status === 401 ||
|
||||||
|
response.status === 403 ||
|
||||||
|
response.status === 422
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +222,8 @@ export const delete_object = async function delete_object({
|
|||||||
? json.data
|
? json.data
|
||||||
: json;
|
: json;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// Ensure per-attempt timeout is always cleared on failure.
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
console.error(`API DELETE error on attempt ${attempt}:`, error);
|
console.error(`API DELETE error on attempt ${attempt}:`, error);
|
||||||
|
|
||||||
if (attempt === retry_count) {
|
if (attempt === retry_count) {
|
||||||
@@ -181,9 +231,12 @@ export const delete_object = async function delete_object({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_lvl) {
|
// Backoff before retrying. Caps at 8s to match GET/POST/PATCH policy.
|
||||||
console.log(`Retrying... (${attempt}/${retry_count})`);
|
const delay_ms = Math.min(2000 * attempt, 8000);
|
||||||
}
|
console.log(
|
||||||
|
`API DELETE: Retrying in ${delay_ms}ms... (attempt ${attempt}/${retry_count})`
|
||||||
|
);
|
||||||
|
await new Promise<void>((resolve) => setTimeout(resolve, delay_ms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user