К содержимому
Hub
All notes

August 22, 2026 · 2 min read

Double booking is fixed by an index, not by a check

SQLSQLitePythonСостояние гонки

In the restaurant booking bot the task looks simple: do not let the same table be taken twice for the same slot.

The first solution suggests itself. Before writing, ask the database whether the slot is free; if it is taken, refuse.

It is wrong, and wrong invisibly. Time passes between the query and the insert. Two guests who press the button at almost the same moment both get "free", and both are written. The bug does not reproduce on a quiet day and will certainly happen on a Friday evening.

The right place for the rule is the database itself. A unique index on table, date and time physically prevents the second row: the second insert fails with a uniqueness error, and all that is left is to catch it and show a readable message.

There is one subtlety — cancelled bookings. Include them in the index and a cancelled table can never be booked again, because the row is still there. A partial index solves it: the WHERE clause excludes cancelled rows from the constraint while leaving them in the table.

The general rule that follows: if a constraint must always hold, it belongs in the database, not in application code. Application code runs in several instances and in arbitrary order; a unique index runs in one place, with no exceptions.

SQL
-- Один столик нельзя занять дважды на один и тот же слот.-- Отменённые брони из проверки исключаются.---- Проверять занятость запросом перед вставкой ненадёжно: между-- проверкой и записью успевает вклиниться второй гость. Здесь-- запрет живёт в самой базе, и обойти его нельзя.CREATE UNIQUE INDEX IF NOT EXISTS idx_booking_slot    ON bookings (table_id, book_date, book_time)    WHERE status <> 'cancelled';