Preventing double-bookings at scale with atomic slot-locking in PostgreSQL
When building ABWORLDNOTION, a photography booking platform, one of the most critical challenges was ensuring that a specific time slot could only be booked by one client. It sounds simple until you factor in concurrent traffic.
If two users click "Book 10:00 AM" at the exact same millisecond, and your code simply does a `SELECT` to check availability followed by an `INSERT` to create the booking, you'll end up double-booking. The gap between reading the state and writing the new state is a race condition.
The Database is the Source of Truth
Instead of trying to implement complex application-level locks (which fail when you scale to multiple Node.js instances), I pushed the locking logic down to PostgreSQL.
By using `SELECT ... FOR UPDATE`, I ensured that the database itself serialized requests trying to access the same time slot row. If User A grabs the lock, User B's request blocks until User A's transaction either commits or rolls back.
BEGIN;
SELECT * FROM time_slots
WHERE id = $1 AND is_available = true
FOR UPDATE SKIP LOCKED;
-- If a row is returned, we have the lock. Update it to false.
UPDATE time_slots SET is_available = false WHERE id = $1;
COMMIT;
Using `SKIP LOCKED` was the final piece of the puzzle. Instead of User B waiting endlessly (and potentially timing out), their query instantly returns no rows, allowing the application to immediately tell them "This slot was just booked by someone else."
What I actually learned
Concurrency problems are best solved at the lowest possible layer. Application code is stateless and distributed; the database is your single source of truth. Rely on its ACID guarantees.
Written by Basit Tijani. Find me on GitHub or LinkedIn.