patterntypescriptMajor
Prevent double booking in a scheduling page without exclusion constraints: per-host advisory locks plus re-running the same slot function inside the transaction
Viewed 0 times
double bookingadvisory lockbooking pagetime slotsrace conditionidempotency
Problem
A public booking page computes free time slots from calendar busy times and lets anonymous visitors book. Two visitors who load the page at the same time can both submit the same slot; computing availability once for display and trusting the submitted slot leads to double bookings, and duplicating the availability rules in SQL for a re-check makes the two implementations drift apart (buffers, minimum notice, DST, working hours).
Solution
Keep one pure function (e.g. TypeScript) that takes settings, busy intervals and 'now' and returns slots. Use it for display. On submit, open a transaction, take pg_advisory_xact_lock on a key per candidate host (sorted by id to avoid deadlocks between pages sharing members), re-read busy intervals inside the transaction, call the same pure function for that day, and accept only if the requested start is still a slot; then insert the calendar event in the same transaction before commit. A concurrent request waits on the lock, then sees the new event and is refused. Make the submit idempotent with a request id generated when the form is rendered (unique column). Send e-mails only after commit through an outbox table so a restart never sends twice. Never trust duration/host/slot from the browser; recompute them server-side.
Why
Availability depends on many rules that are easy to get subtly different in two languages; serializing per host with advisory locks gives correctness without btree_gist exclusion constraints, and reusing one function guarantees display and validation agree.
Gotchas
- Buffer time must be applied on both sides of existing events, not only after them
- Convert local window times with a DST-aware helper: nonexistent spring-forward times and repeated fall-back times
- Offline/request-only bookings that create no event bypass the protection by design
- Lock all candidate hosts in a stable order (round-robin and group pages)
Revisions (0)
No revisions yet.