feat: implement exponential backoff and retry logic for file uploads
This commit is contained in:
@@ -113,6 +113,42 @@ export default function UploadDatasetEntriesModal({
|
|||||||
file: File,
|
file: File,
|
||||||
index: number,
|
index: number,
|
||||||
queue: Array<{ file: File; status: 'pending' | 'uploading' | 'completed' | 'failed'; progress: number; message?: string }>
|
queue: Array<{ file: File; status: 'pending' | 'uploading' | 'completed' | 'failed'; progress: number; message?: string }>
|
||||||
|
): Promise<{ tempDir: string }> {
|
||||||
|
const MAX_RETRIES = 3;
|
||||||
|
|
||||||
|
return (async () => {
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||||
|
try {
|
||||||
|
const result = await uploadAttempt(file, index, queue, attempt, MAX_RETRIES);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error instanceof Error ? error : new Error('Unknown error');
|
||||||
|
|
||||||
|
if (attempt < MAX_RETRIES) {
|
||||||
|
// Exponential backoff: 1s, 2s, 4s
|
||||||
|
const delay = Math.pow(2, attempt - 1) * 1000;
|
||||||
|
queue[index].message = `Retrying in ${delay / 1000}s... (attempt ${attempt}/${MAX_RETRIES})`;
|
||||||
|
setUploadQueue([...queue]);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
queue[index].progress = 0; // Reset progress for next attempt
|
||||||
|
setUploadQueue([...queue]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw lastError || new Error('Upload failed after all retries');
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadAttempt(
|
||||||
|
file: File,
|
||||||
|
index: number,
|
||||||
|
queue: Array<{ file: File; status: 'pending' | 'uploading' | 'completed' | 'failed'; progress: number; message?: string }>,
|
||||||
|
attempt: number,
|
||||||
|
maxRetries: number
|
||||||
): Promise<{ tempDir: string }> {
|
): Promise<{ tempDir: string }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -126,6 +162,9 @@ export default function UploadDatasetEntriesModal({
|
|||||||
if (event.lengthComputable) {
|
if (event.lengthComputable) {
|
||||||
const progress = Math.round((event.loaded / event.total) * 100);
|
const progress = Math.round((event.loaded / event.total) * 100);
|
||||||
queue[index].progress = progress;
|
queue[index].progress = progress;
|
||||||
|
if (attempt > 1) {
|
||||||
|
queue[index].message = `Uploading (retry ${attempt}/${maxRetries})`;
|
||||||
|
}
|
||||||
setUploadQueue([...queue]);
|
setUploadQueue([...queue]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -153,7 +192,7 @@ export default function UploadDatasetEntriesModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener('error', () => {
|
xhr.addEventListener('error', () => {
|
||||||
reject(new Error('Upload failed'));
|
reject(new Error('Network error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener('abort', () => {
|
xhr.addEventListener('abort', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user