Files
omniai-server/src/settlementWorker.js
T
2026-06-02 13:14:10 +08:00

90 lines
2.4 KiB
JavaScript

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 };