Files
anouma/lib/booking/lock.ts
T

27 lines
1.2 KiB
TypeScript

import { sql } from "drizzle-orm";
import type { PayloadRequest } from "payload";
/**
* Acquires a Postgres transaction-scoped advisory lock so that two
* concurrent requests confirming a booking for the same day can't both pass
* the "is this slot free?" check before either has committed (Payload's
* default transaction isolation is READ COMMITTED, which alone would allow
* that race). The lock is automatically released when the transaction ends.
*
* Must be called from inside a Payload hook/operation that already has an
* active `req.transactionID` (true for collection create/update by default).
*/
export async function acquireBookingDayLock(req: PayloadRequest, dateKey: string): Promise<void> {
const txID = req.transactionID ? String(await req.transactionID) : undefined;
if (!txID) throw new Error("A booking lock requires an active transaction");
const adapter = req.payload.db as unknown as {
sessions?: Record<string, { db: { execute: (query: unknown) => Promise<unknown> } }>;
drizzle: { execute: (query: unknown) => Promise<unknown> };
};
const tx = adapter.sessions?.[txID]?.db;
if (!tx) throw new Error("Booking transaction not found");
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${dateKey}))`);
}