Harden source cleanup durability barriers

Separate provisional upload success from persistently confirmed cleanup state, invalidate prior confirmations before retries, and promote completion only inside the final queue persistence handshake. Preserve source files when history or queue persistence fails and add regression coverage for restart, retry, rollback, and real Electron finalization paths.
This commit is contained in:
Sucukdeluxe
2026-08-13 19:45:31 +02:00
parent bfb3a39fed
commit 67978cb81f
7 changed files with 540 additions and 91 deletions
+7 -3
View File
@@ -122,7 +122,7 @@ let lastSessionSummary = null;
let sourceDeleteJournal = null;
const pendingUploadFinalizations = new Map();
function requestUploadFinalization(summary) {
function requestUploadFinalization(summary, historyPersisted) {
const finalizationId = `upload-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
return new Promise((resolve) => {
const timer = setTimeout(() => {
@@ -136,7 +136,7 @@ function requestUploadFinalization(summary) {
resolve(value);
}
});
safeSend('upload-batch-done', { summary, finalizationId });
safeSend('upload-batch-done', { summary, finalizationId, historyPersisted: historyPersisted === true });
});
}
const activeUploadProducerTrackers = new Set();
@@ -2276,7 +2276,7 @@ ipcMain.handle('start-upload', async (_event, payload) => {
for (const value of _progressByJob.values()) finalProgressBatch.push(value);
_progressByJob.clear();
if (finalProgressBatch.length) safeSend('upload-progress-batch', finalProgressBatch);
const queuePersisted = await requestUploadFinalization(summary);
const queuePersisted = await requestUploadFinalization(summary, historyPersisted);
try { await configStore.saveUploadRecovery(null); } catch (error) { debugLog(`upload recovery state could not be cleared: ${error.message}`); }
if (!queuePersisted) debugLog('upload finalization blocked: renderer queue acknowledgement missing');
await sourceCleanup.finishBatch({ historyPersisted, queuePersisted });
@@ -2978,6 +2978,10 @@ ipcMain.handle('complete-upload-finalization', async (_event, payload) => {
const finalizationId = payload && payload.finalizationId;
const pending = finalizationId && pendingUploadFinalizations.get(finalizationId);
if (!pending) return false;
if (payload.ready === false) {
pending.resolve(false);
return false;
}
try {
await configStore.savePendingQueue(payload.pendingQueue ?? null);
pending.resolve(true);