Preventing Race Conditions & Double-Booking Anomalies in High-Traffic Clinic Calendars
How BharatOPD protects scheduling pipelines using transactional locks and Redis locking primitives under heavy appointment volumes.

Preventing Race Conditions & Double-Booking Anomalies in High-Traffic Clinic Calendars
When scaling a healthcare SaaS system, the scheduling engine is the core operations layer. During morning booking spikes, multiple patient portals, clinic receptionists, and call centers frequently attempt to claim the same doctor consultation time slot simultaneously.
Allowing two patients to book the same appointment slot creates a major operational issue.
This guide explains how BharatOPD uses database transactional concurrency locks and Redis locking primitives to ensure zero-double-booking integrity, even under extreme concurrent scheduling loads.
1. Understanding the Race Condition Danger
In standard systems, a booking controller follows a simple flow:
- Check if the slot is free (
SELECT status FROM appointments WHERE slot_time = '09:00 AM'). - If free, book the appointment (
UPDATE appointments SET status = 'booked', patient_id = 456 WHERE slot_time = '09:00 AM').
Patient A (Request 09:00 AM) ──► [Read DB: Free] ──────────► [Write DB: Booked for A]
Patient B (Request 09:00 AM) ──► [Read DB: Free] (Collision!) ──► [Write DB: Booked for B]
If both read queries execute before either update writes, both patients receive a booking confirmation, causing a double-booking collision.
2. The Solution: Pessimistic Row Locking in PostgreSQL
To prevent slot collisions, BharatOPD uses Pessimistic Row-Level Locking inside a database transaction block. By appending a FOR UPDATE modifier to the check query, PostgreSQL blocks any concurrent transactions from reading or updating that specific row until the active transaction completes.
const db = require('../lib/database');
async function bookAppointmentSecurely(patientId, slotId) {
return db.transaction(async (trx) => {
// 1. Read and lock the row, blocking concurrent requests
const slot = await trx('appointments')
.where('id', slotId)
.select('status', 'doctor_id', 'slot_time')
.forUpdate() // CRITICAL: Applies lock block
.first();
if (!slot) {
throw new Error('Consultation slot does not exist');
}
if (slot.status !== 'available') {
throw new Error('This consultation slot is already booked');
}
// 2. Perform the update secure in the knowledge that the row is locked
const [booking] = await trx('appointments')
.where('id', slotId)
.update({
patient_id: patientId,
status: 'booked',
booked_at: new Date()
})
.returning('*');
return booking;
});
}
With this approach, when Patient B attempts to read the row, their database connection yields and waits for Patient A’s transaction to either commit or roll back, preventing any double-booking anomalies.
3. High-Speed Distributed Locks with Redis (Redlock)
For clinic networks with heavy traffic where doctor calendars span multiple database instances, we implement distributed locking using Redis Redlock primitives.
Before initiating a database transaction, our API acquires a temporary lock token in Redis. If another request attempts to acquire the lock for the same doctor:slot key, the request is immediately blocked, protecting our primary databases from lock waits and query overhead.
4. The Engineering Takeaway
Preventing data anomalies under heavy concurrent traffic requires deep database engineering expertise. By choosing AppRinger, you align with product operators who understand how to design and build highly resilient, transaction-safe platforms. Contact us to learn how we can secure your data and operations today.