← All notes

· 1 min read

Double booking: a transaction plus a partial UNIQUE index

How booking-slots protects against two clients booking the same time at once, and how that protection is tested.

Project: booking-slots

The classic race: two clients see a free slot at the same moment and both press "book". A single "is it free" check in code is not enough: a second request can slip in between the check and the insert.

First line: a transaction

In Book, recalculating the free slots and inserting the booking happen in one transaction. A slot that is not among the free ones yields ErrTaken (or ErrPast when the time has already passed).

Second line: an index

The database itself refuses to create a duplicate:

CREATE UNIQUE INDEX IF NOT EXISTS ux_bookings_active_start
  ON bookings(date, start_min) WHERE status = 'active';

The index is partial: it applies only to active bookings, so a cancelled booking frees the time. If the insert hits the index, the code turns the error into the same ErrTaken and the client gets 409.

How it is tested

A test launches 30 goroutines for the same time: exactly one booking must succeed.

Boundaries

This protects a single resource: all services share one schedule, and a multi-specialist model would need a different index. The project's limitations say so.