From c2694680142f6938f42b1716262017c03edc436d Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Wed, 3 Jun 2026 14:03:32 +0000 Subject: [PATCH] =?UTF-8?q?fix(scheduler):=20orphan=20grace=20window=20mus?= =?UTF-8?q?t=20use=20recorder.updated=5Fat=20not=20asset.updated=5Fat=20?= =?UTF-8?q?=E2=80=94=20asset=20is=20created=20at=20recording=20START=20not?= =?UTF-8?q?=20STOP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/mam-api/src/scheduler.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/services/mam-api/src/scheduler.js b/services/mam-api/src/scheduler.js index d77726c..3865bb4 100644 --- a/services/mam-api/src/scheduler.js +++ b/services/mam-api/src/scheduler.js @@ -137,10 +137,11 @@ async function tick() { // Orphaned live assets: recorder stopped but asset still 'live'. // Happens when the capture sidecar crashes before finalize() runs. - // Wait 90s grace before marking error — this prevents a mam-api restart - // from racing the capture container's finalize() POST, which can take up - // to 30s for large files on a slow upload or busy node. - const ORPHAN_GRACE_SECONDS = parseInt(process.env.ORPHAN_GRACE_SECONDS || '90', 10); + // Grace window is measured from when the RECORDER was last updated + // (i.e. when it transitioned to stopped), not from asset creation. + // This prevents a race where the scheduler fires before the capture + // container's finalize POST lands (can take 30-60s on large files). + const ORPHAN_GRACE_SECONDS = parseInt(process.env.ORPHAN_GRACE_SECONDS || '120', 10); const orphanResult = await client.query( `UPDATE assets a SET status = 'error', updated_at = NOW() @@ -148,7 +149,7 @@ async function tick() { WHERE a.status = 'live' AND a.display_name = r.current_session_id AND r.status = 'stopped' - AND a.updated_at < NOW() - ($1 || ' seconds')::INTERVAL + AND r.updated_at < NOW() - ($1 || ' seconds')::INTERVAL RETURNING a.id, a.display_name`, [ORPHAN_GRACE_SECONDS] );