import { collections } from "./db/db"; const CLOCK_SKEW_TOLERANCE = 30000; export async function acquireLock( lockName: string, ttl: number, timeout = 0, token = Math.random().toString(36).slice(2) ): Promise { try { const now = Date.now(); const r = await collections.locks.findOneAndUpdate( { _id: lockName, value: token }, { $set: { expire: new Date(now + ttl + CLOCK_SKEW_TOLERANCE), }, $currentDate: { timestamp: true }, }, { upsert: true, returnDocument: "after" } ); if (Math.abs(r.value.timestamp.getTime() - now) > CLOCK_SKEW_TOLERANCE) throw new Error("Database clock skew too great"); } catch (err) { if (err.code !== 11000) throw err; if (!(timeout > 0)) return null; const w = 50 + Math.random() * 50; await new Promise((resolve) => setTimeout(resolve, w)); return acquireLock(lockName, ttl, timeout - w, token); } return token; } export async function releaseLock( lockName: string, token: string ): Promise { const res = await collections.locks.deleteOne({ _id: lockName, value: token, }); if (res.deletedCount !== 1) throw new Error("Lock expired"); } export async function lockExists(lockName: string): Promise { const res = await collections.locks.findOne({ _id: lockName }); return res != null; }