Merge pull request 'fix: capture job baseline before POST to avoid race condition' (#60) from feat/jobs-system into dev
All checks were successful
CI / test (push) Successful in 16s
CI / build-dev (push) Successful in 29s

Reviewed-on: #60
This commit was merged in pull request #60.
This commit is contained in:
2026-03-28 20:43:26 -04:00

View File

@@ -289,6 +289,10 @@ async function saveInstance() {
hardware_acceleration: +document.getElementById('f-hardware-accel').checked, hardware_acceleration: +document.getElementById('f-hardware-accel').checked,
}; };
// Snapshot job state before creation — jobs fire immediately after the 201
// so the baseline must be captured before the POST, not after.
const jobBaseline = !editingVmid ? await _snapshotJobBaseline() : null;
const result = editingVmid const result = editingVmid
? await updateInstance(editingVmid, data) ? await updateInstance(editingVmid, data)
: await createInstance(data); : await createInstance(data);
@@ -298,7 +302,7 @@ async function saveInstance() {
showToast(`${name} ${editingVmid ? 'updated' : 'created'}`, 'success'); showToast(`${name} ${editingVmid ? 'updated' : 'created'}`, 'success');
closeModal(); closeModal();
if (!editingVmid) await _waitForOnCreateJobs(); if (jobBaseline) await _waitForOnCreateJobs(jobBaseline);
if (currentVmid && document.getElementById('page-detail').classList.contains('active')) { if (currentVmid && document.getElementById('page-detail').classList.contains('active')) {
await renderDetailPage(vmid); await renderDetailPage(vmid);
@@ -307,16 +311,18 @@ async function saveInstance() {
} }
} }
async function _waitForOnCreateJobs() { async function _snapshotJobBaseline() {
const jobs = await fetch('/api/jobs').then(r => r.json());
return new Map(jobs.map(j => [j.id, j.last_run_id ?? null]));
}
async function _waitForOnCreateJobs(baseline) {
const jobs = await fetch('/api/jobs').then(r => r.json()); const jobs = await fetch('/api/jobs').then(r => r.json());
const relevant = jobs.filter(j => { const relevant = jobs.filter(j => {
try { return JSON.parse(j.config || '{}').run_on_create; } catch { return false; } try { return JSON.parse(j.config || '{}').run_on_create; } catch { return false; }
}); });
if (!relevant.length) return; if (!relevant.length) return;
// Snapshot run IDs before jobs fire so we can detect new completions
const baseline = new Map(relevant.map(j => [j.id, j.last_run_id ?? null]));
const deadline = Date.now() + 30_000; const deadline = Date.now() + 30_000;
while (Date.now() < deadline) { while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, 500)); await new Promise(r => setTimeout(r, 500));