const billing = require("./billing"); const SETTLEMENT_INTERVAL_MS = 5 * 60 * 1000; const LEAK_THRESHOLD_MINUTES = 10; let timerId = null; async function settleOrphanedLeases() { const { pool, withTransaction } = require("./db"); const now = new Date(); const leakCutoff = new Date(now.getTime() - LEAK_THRESHOLD_MINUTES * 60 * 1000).toISOString(); const { rows: releasedUnsettled } = await pool.query(` SELECT id FROM key_leases WHERE settled = 0 AND released_at IS NOT NULL `); const { rows: leaked } = await pool.query( ` SELECT id, key_id FROM key_leases WHERE settled = 0 AND released_at IS NULL AND leased_at < $1 `, [leakCutoff], ); if (leaked.length > 0) { await withTransaction(async (client) => { for (const lease of leaked) { await client.query("UPDATE key_leases SET released_at = NOW() WHERE id = $1", [lease.id]); await client.query( "UPDATE api_keys SET active_count = GREATEST(0, active_count - 1) WHERE id = $1", [lease.key_id], ); } }); } const allOrphaned = [...releasedUnsettled, ...leaked]; let settled = 0; let failed = 0; for (const lease of allOrphaned) { try { await billing.forceSettleLease(lease.id); settled++; } catch (err) { console.error("[settlementWorker] failed to settle lease", lease.id, err.message); failed++; } } if (settled > 0 || failed > 0) { console.log( `[settlementWorker] settled=${settled} failed=${failed} released_unsettled=${releasedUnsettled.length} leaked=${leaked.length}`, ); } } function startSettlementWorker() { if (timerId) return; settleOrphanedLeases().catch((err) => { console.error("[settlementWorker] initial run failed", err.message); }); timerId = setInterval(() => { settleOrphanedLeases().catch((err) => { console.error("[settlementWorker] periodic run failed", err.message); }); }, SETTLEMENT_INTERVAL_MS); if (timerId.unref) { timerId.unref(); } console.log( `[settlementWorker] started (interval=${SETTLEMENT_INTERVAL_MS}ms, leakThreshold=${LEAK_THRESHOLD_MINUTES}min)`, ); } function stopSettlementWorker() { if (timerId) { clearInterval(timerId); timerId = null; console.log("[settlementWorker] stopped"); } } module.exports = { startSettlementWorker, stopSettlementWorker, settleOrphanedLeases };