Pessimistic locking for the claim, optimistic for the lifecycle
Problem. Concurrent bookings race between checking a professional is free and inserting the booking.
- Optimistic locking with a version column and retries
- A pessimistic row lock
- A Postgres exclusion constraint enforcing non-overlap in the database
Decision. A pessimistic write lock on the professional row at claim time, paired with the overlap query. Booking status changes use optimistic locking with a version column instead.
Why. When many requests contend for one popular professional, optimistic retries thrash: every loser re-reads and collides again. A pessimistic lock serializes that hot path once and deterministically. Status transitions are the opposite case. They rarely collide, so a version column is the lighter, lock-free fit. The exclusion constraint is the strongest database-level answer and the move at real scale, but I kept the guarantee in the application to stay portable across databases.