Skip to content

Room booking race condition — two concurrent requests can double-book the same slot #242

@amaydixit11

Description

@amaydixit11

Issue

PR #233 recently added "Smart Room Booking & Clash Detection". I reviewed the backend code in:

  • backend/controllers/roomBookingController.js
  • backend/routes/roomBooking.js

Potential Race Condition

If the clash detection works like this:

// 1. Check for conflicts
const conflicts = await Booking.find({ roomId, timeSlot });
if (conflicts.length > 0) return error;

// 2. Create booking
await Booking.create({ roomId, timeSlot });

Then two simultaneous requests will both pass the check (step 1) and both create bookings (step 2) — resulting in a double-booked room.

What to check

  1. Database-level constraint: Does the mongoose schema have a unique compound index on [room, startDate, startTime]?

    bookingSchema.index({ roomId: 1, date: 1, startTime: 1 }, { unique: true });
  2. Transaction usage: Is the clash-check + create wrapped in a MongoDB transaction?

    const session = await mongoose.startSession();
    session.withTransaction(async () => {
      // check + create atomically
    });
  3. Authorization: Can any student book any room, or are there permissions? Does isAuthenticated.js + authorizeRole.js middleware protect the room booking endpoints?

  4. Clash detection logic: Does it check:

    • Overlapping time ranges (not just exact matches)?
    • Different days?
    • Recurring bookings?

Fix

Add a unique compound index and use transactions:

// In schema
bookingSchema.index({ roomId: 1, date: 1, startTime: 1, endTime: 1 });

// In controller
const session = await mongoose.startSession();
try {
  await session.withTransaction(async () => {
    const conflict = await Booking.findOne({
      roomId, date,
      $or: [
        { startTime: { $lt: newEndTime }, endTime: { $gt: newStartTime } }
      ]
    }).session(session);
    
    if (conflict) throw new Error('Room already booked');
    
    await Booking.create([{ roomId, date, startTime, endTime }], { session });
  });
} finally {
  session.endSession();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    advancedComplex issues requiring experienced contributorsbugSomething isn't workingenhancementNew feature or requestsecuritySecurity vulnerabilities

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions