27 lines
1.2 KiB
TypeScript
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}))`);
|
|
}
|