Upload: timeout + retry + ne ustavi loop ob enem fail-u
User: 'zakaj se je ustavilo? Naložil sem več kot 70.' Diagnoza: 12/70+ je prišlo do server-ja (vsi 200 OK). Browser-side problem: en upload je stuck → cel for-loop blokiran. Fixes: 1. xhr.timeout = 10min per file (prej: večnost) 2. xhr.ontimeout, xhr.upload.ontimeout — proper error handling 3. NEW: uploadFileWithRetry() — 2x retry z 2s/4s eksponentnim delay-om za očasne mrežne odpovedi 4. Catch v loop ne kliče liveFail() (kar bi naredil disabled submit-btn) ampak samo showLive() z 'preskočil' sporočilo → loop nadaljuje 5. Console.warn v retry attempts za debugging Sedaj če eno failes: - Retry 2x avtomatsko (2s + 4s delay) - Če še vedno ne uspe → preskoči to datoteko, nadaljuj z naslednjo - Pri koncu vidiš katere so failed (v console + showLive)
This commit is contained in:
parent
2de58ca7a5
commit
c58875c072
@ -843,7 +843,7 @@
|
|||||||
fd.append("batch_id", batchId);
|
fd.append("batch_id", batchId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const uploadResp = await uploadFileXHR(fd, (loaded, total) => {
|
const uploadResp = await uploadFileWithRetry(fd, (loaded, total) => {
|
||||||
const filePct = (loaded / total) * 100;
|
const filePct = (loaded / total) * 100;
|
||||||
showLive(
|
showLive(
|
||||||
`Nalaganje ${i + 1}/${totalFiles}`,
|
`Nalaganje ${i + 1}/${totalFiles}`,
|
||||||
@ -861,8 +861,12 @@
|
|||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to upload ${f.name}: ${e.message}`);
|
console.error(`Failed to upload ${f.name}: ${e.message}`);
|
||||||
liveFail(`Napaka pri ${f.name}: ${e.message}`);
|
showLive(
|
||||||
// Continue z naslednjimi
|
`⚠ Preskočil ${i + 1}/${totalFiles}`,
|
||||||
|
`${f.name}: ${e.message} (nadaljujem z naslednjim)`,
|
||||||
|
((i + 1) / totalFiles) * 100
|
||||||
|
);
|
||||||
|
// Continue z naslednjimi (ne breakaj cel loop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,6 +891,8 @@
|
|||||||
function uploadFileXHR(formData, onProgress) {
|
function uploadFileXHR(formData, onProgress) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
// 10 min timeout per file (za velike komade)
|
||||||
|
xhr.timeout = 10 * 60 * 1000;
|
||||||
xhr.upload.onprogress = e => {
|
xhr.upload.onprogress = e => {
|
||||||
if (e.lengthComputable && onProgress) {
|
if (e.lengthComputable && onProgress) {
|
||||||
onProgress(e.loaded, e.total);
|
onProgress(e.loaded, e.total);
|
||||||
@ -896,13 +902,33 @@
|
|||||||
if (xhr.status === 200) resolve(xhr.responseText);
|
if (xhr.status === 200) resolve(xhr.responseText);
|
||||||
else reject(new Error(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));
|
else reject(new Error(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));
|
||||||
};
|
};
|
||||||
xhr.onerror = () => reject(new Error("Upload error"));
|
xhr.onerror = () => reject(new Error("Network error (offline?)"));
|
||||||
|
xhr.ontimeout = () => reject(new Error("Timeout (10min)"));
|
||||||
xhr.upload.onerror = () => reject(new Error("Upload transfer error"));
|
xhr.upload.onerror = () => reject(new Error("Upload transfer error"));
|
||||||
|
xhr.upload.ontimeout = () => reject(new Error("Upload timeout"));
|
||||||
xhr.open("POST", "/api/upload");
|
xhr.open("POST", "/api/upload");
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry helper: če upload faila, poskusi 2x ponovno z malim delay-om
|
||||||
|
async function uploadFileWithRetry(formData, onProgress, maxRetries = 2) {
|
||||||
|
let lastErr = null;
|
||||||
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
return await uploadFileXHR(formData, onProgress);
|
||||||
|
} catch (e) {
|
||||||
|
lastErr = e;
|
||||||
|
console.warn(`Upload attempt ${attempt + 1} failed: ${e.message}`);
|
||||||
|
if (attempt < maxRetries) {
|
||||||
|
// Eksponentni delay: 2s, 4s
|
||||||
|
await new Promise(r => setTimeout(r, 2000 * (attempt + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw lastErr;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Watch job (SSE) ────────────────────────────
|
// ─── Watch job (SSE) ────────────────────────────
|
||||||
function watchJob(jobId) {
|
function watchJob(jobId) {
|
||||||
const evt = new EventSource(`/api/stream/${jobId}`);
|
const evt = new EventSource(`/api/stream/${jobId}`);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user