The situation
Dentalia operates one of the largest dental clinic networks in Latin America. When you book an appointment through their platform, you're choosing from real-time availability across 65+ clinics and hundreds of individual dentists — each with their own schedules, blocked days, and special availability exceptions. The old system had a race condition at the heart of its booking flow. When two patients tried to book the same slot with the same doctor at the same moment — a common occurrence during peak appointment windows — the system would check availability for both simultaneously, find the slot open for both, and confirm both bookings. The clinic would then have two patients showing up for the same chair. We redesigned the availability schema and the booking transaction from the ground up. Availability is now computed from schedule templates with exception overrides (vacation days, reduced hours, special clinic events) — a much more efficient query pattern than the previous approach of storing every individual slot as a database row. The booking transaction uses SELECT FOR UPDATE pessimistic locking: when a patient initiates a booking, the slot is locked at the database row level for the duration of the transaction. No concurrent booking can touch it. If the lock can't be acquired, the patient sees 'this slot was just taken' and is shown alternatives — before they complete the form, not after. The entire backend is Dockerized, eliminating the environment inconsistencies that were causing intermittent bugs in production.
What had to change
Race conditions in concurrent booking caused double bookings regularly during peak hours. Two patients would book the same slot simultaneously, both receive confirmations, and both show up at the clinic. Besides the patient experience damage, each incident required manual rescheduling and a clinic team member's time. The schema also didn't scale well to 65+ clinics — availability queries were joining massive tables.
What we changed
Schema redesign: availability computed from schedule templates + exception overrides (not from a slot-per-row table). Pessimistic locking on the booking transaction: SELECT FOR UPDATE on slot acquisition, held for the full duration. Template-based availability queries run faster and scale to any number of clinics. Docker deployment for consistency across all environments.
What the client gained
- 01 Pessimistic DB locking — race conditions are impossible
- 02 65+ clinics and hundreds of doctors on one platform
- 03 Template-based availability — clean schema at scale
- 04 Docker — consistent from dev to production