- Next.js 16 App Router site with the ANOUMA design system - Payload CMS (PostgreSQL) for offers, events, posts and page content - WebRTC video-call system with custom signaling server - SMTP email reminders and booking-request notifications - Customer accounts, calendar-based availability, and booking workflow
26 lines
1.2 KiB
TypeScript
26 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) return; // No active transaction — nothing to lock against.
|
|
|
|
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 ?? adapter.drizzle;
|
|
|
|
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${dateKey}))`);
|
|
}
|