fix: yeniden başlatma sonrası açık kalan session'ları temizle

Bir redeploy, devam eden capture'ı temizlik kodu hiç çalışmadan
öldürüyordu — StreamSession.endedAt sonsuza dek null kalıyordu. Bu
sadece panelde yanlış "kayıtta" göstermekle kalmıyor, startSession()
açık bir session gördüğünde o kanal için yeni session başlatmayı
tamamen atlıyor — yani o kanal bir daha asla kayda alınamıyordu.
api-daemon başlarken artık tüm açık session'ları kapatıyor (taze bir
process başlangıcı = gerçekte devam eden capture yok demektir).
BullMQ lockDuration da 24 saatten 10 dakikaya indirildi — worker
canlıyken kilit otomatik yenileniyor, düşük değer sadece ölü bir
worker'ın job'ının ne kadar hızlı kurtarılacağını etkiliyor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 18:54:47 +03:00
co-authored by Claude Sonnet 5
parent d05aed72ff
commit d562c05882
2 changed files with 28 additions and 3 deletions
+7 -3
View File
@@ -181,8 +181,12 @@ export function startStreamIngestWorker(): Worker<StreamIngestJob> {
// count; the real ceiling is the server's CPU/bandwidth for running that
// many yt-dlp+ffmpeg pairs at once, not this number.
concurrency: env.maxConcurrentCaptures,
// Captures run for the lifetime of the stream (can be hours) — extend the
// default lock well beyond BullMQ's stalled-job assumptions for an MVP.
lockDuration: 24 * 60 * 60 * 1000,
// BullMQ renews this lock automatically while a worker is alive, so a
// low value doesn't cap how long a capture can run — it only controls
// how fast a *dead* worker's job is detected as stalled and recovered.
// (An earlier 24h value meant a killed process's job wouldn't be
// recovered for up to 24h; closeOrphanedSessions() in index.ts is what
// actually unblocks that channel's StreamSession in the meantime.)
lockDuration: 10 * 60 * 1000,
});
}
+21
View File
@@ -30,7 +30,28 @@ async function forceLiveBypass(youtubeUrl: string) {
});
}
/**
* A container restart (e.g. a redeploy) kills any in-progress capture
* without running its cleanup code, leaving that StreamSession's endedAt
* stuck at null forever. Besides showing "still recording" in the panel,
* startSession() treats any open session as a reason to skip starting a new
* one for that channel — so an orphaned session permanently blocks that
* channel from ever being recorded again until this runs. Since a fresh
* process start means no capture can actually still be in progress, every
* open session at boot is stale by definition and gets closed out.
*/
async function closeOrphanedSessions() {
const { count } = await prisma.streamSession.updateMany({
where: { endedAt: null },
data: { endedAt: new Date() },
});
if (count > 0) {
console.log(`[index] closed ${count} orphaned session(s) left open by a previous process`);
}
}
async function main() {
await closeOrphanedSessions();
startServer();
startStreamIngestWorker();