/** Minimal async mutex: serializes critical sections (multi-row upserts) so Postgres never sees two overlapping lock orders. */ export class Mutex { private tail: Promise = Promise.resolve(); run(fn: () => Promise): Promise { const next = this.tail.then(fn, fn); this.tail = next.then( () => undefined, () => undefined, ); return next; } } /** One writer at a time for each hot table family. */ export const barsMutex = new Mutex(); export const quotesMutex = new Mutex(); export const masterMutex = new Mutex(); // instruments, companies, symbol_aliases