Fix concurrent upload batch accounting
Reserve accepted job IDs before asynchronous upload preparation so parallel additions cannot start duplicate jobs or replace their abort controllers. Track late-added jobs in the batch total and keep failure counts non-negative, with focused race and summary regressions.
This commit is contained in:
@@ -53,6 +53,8 @@ class UploadManager extends EventEmitter {
|
||||
this._doodApiKeyCache = new Map(); // accountId/username -> derived doodstream API key ('' = tried, none)
|
||||
this._baselineCache = new Map(); // hoster:apiKey -> Promise<Set<file_code>> (one fetch shared across all jobs in batch)
|
||||
this._recoveryClaims = createRecoveryClaimRegistry();
|
||||
this._batchJobIds = new Set();
|
||||
this._batchTotal = 0;
|
||||
}
|
||||
|
||||
updateAccountPools(accountPools) {
|
||||
@@ -336,6 +338,8 @@ class UploadManager extends EventEmitter {
|
||||
async startBatch(tasks, opts = {}) {
|
||||
const pendingCancelledJobIds = new Set(this.pendingCancelledJobIds);
|
||||
const pendingCancelAll = this.pendingCancelAll;
|
||||
this._batchJobIds = new Set(tasks.map((task) => task.jobId).filter(Boolean));
|
||||
this._batchTotal = tasks.length;
|
||||
this.pendingCancelledJobIds.clear();
|
||||
this.pendingCancelAll = false;
|
||||
this.running = true;
|
||||
@@ -422,7 +426,7 @@ class UploadManager extends EventEmitter {
|
||||
this._emitStats();
|
||||
|
||||
const files = Array.from(results.values());
|
||||
const total = tasks.length;
|
||||
const total = this._batchTotal;
|
||||
const succeeded = files.reduce((count, file) => count + file.results.filter((result) => result.status === 'done').length, 0);
|
||||
const skipped = files.reduce((count, file) => count + file.results.filter((result) => result.status === 'skipped').length, 0);
|
||||
|
||||
@@ -431,7 +435,7 @@ class UploadManager extends EventEmitter {
|
||||
timestamp: new Date().toISOString(),
|
||||
total,
|
||||
succeeded,
|
||||
failed: total - succeeded - skipped,
|
||||
failed: Math.max(0, total - succeeded - skipped),
|
||||
skipped,
|
||||
files
|
||||
};
|
||||
@@ -1461,16 +1465,18 @@ class UploadManager extends EventEmitter {
|
||||
const addResult = { added: 0, alreadyInBatchJobIds: [] };
|
||||
for (const task of tasks) {
|
||||
// Skip if this job is already being processed (prevent duplicates)
|
||||
if (task.jobId && this.jobAbortControllers.has(task.jobId)) {
|
||||
if (task.jobId && this._batchJobIds.has(task.jobId)) {
|
||||
addResult.alreadyInBatchJobIds.push(task.jobId);
|
||||
continue;
|
||||
}
|
||||
if (task.jobId) this._batchJobIds.add(task.jobId);
|
||||
const fileName = path.basename(task.file);
|
||||
if (!results.has(task.file)) {
|
||||
let size = 0;
|
||||
try { size = fs.statSync(task.file).size; } catch {}
|
||||
results.set(task.file, { name: fileName, size, results: [] });
|
||||
}
|
||||
this._batchTotal++;
|
||||
this._additionalPromises.push(this._runJob(task, results, signal));
|
||||
addResult.added++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user