Requirements & System Scope
Functional Scope (In-Scope)
- Dynamic Room Booking: Book meeting rooms for standard dynamic time intervals (start/end timestamp epoch).
- Overlapping Check: Strict overlap check to prevent double-booking rooms.
- Search Availability: Return rooms matching minimum attendance size constraints that are free during requested slots.
- Concurrent Protection: Process concurrent booking calls gracefully, maintaining complete ticket/reservation consistency.
Explicit Boundaries (Out-of-Scope)
- No Dynamic Recurrence Logic: Recurring reservation rules (e.g. daily/weekly repeating meetings) are omitted to focus on structural interval scheduling.
- No Room Equipment Configuration: Ignores specific equipment requests (projectors, smart boards, audio accessories).
Class Diagram & Entity Relationships
Structured structural layout showing decoupled domain scheduling objects:
Loading...
- Value Object (TimeSlot):
TimeSlotacts as a pure value object encapsulating mathematical interval overlaps, preventing logic leaks into parent classes. - Decoupled Locks: Fine-grained locks live inside individual
Roominstances rather than global orchestrators, maximizing performance during concurrent non-colliding reservations.
Design Patterns & SOLID Principles
- Strategy Pattern (Room Selection Strategy): Decouples room selection/allocation algorithms (e.g.
BestFitRoomSelectionStrategyvsFirstFitRoomSelectionStrategy) from the coreMeetingSchedulerorchestrator class, enabling runtime swappability and adhering to the Open-Closed Principle (OCP). - Single Responsibility Principle (SRP):
TimeSlotsolely evaluates time intersections.Roomhandles dynamic occupancy sets and thread synchronization.MeetingSchedulerorchestrates room lists, delegating matching and selection to pluggable strategy instances. - State Encapsulation (ReentrantLock): Reentrant locks protect internal booking structures inside each room, encapsulating the transaction safely.
Core Execution Workflows
1. Room Allocation Flow
- Client invokes
scheduleMeeting(organizer, attendees, timeSlot). - Scheduler iterates through room set, sorting matching entries by size capacity.
- For every qualified room, a booking reservation transaction is attempted:
- Acquire the targeted room's localized Reentrant lock.
- Evaluate interval intersections:
start1 < end2 && start2 < end1. - If collision occurs, release lock immediately and check next room.
- Otherwise, create
Booking, update occupancy records, release lock, and return.
- If all matching rooms are blocked, return an explicit error notification.
Concurrency & Thread Safety Strategy
The critical concurrency vulnerability occurs when multiple threads attempt to book the **same room** for overlapping intervals simultaneously. This can lead to double-bookings if the check and insertion are not performed atomically:
- Fine-Grained Reentrant Locks: Locking individual rooms instead of the entire scheduler allows parallel booking transactions to succeed concurrently across different rooms.
- Atomic Checkout Check: The check-then-act logic (checking overlap and inserting booking) is entirely enclosed inside the lock's scope.
Complete Clean Code Blueprint
Clean reference designs showcasing operational patterns and thread-safety in Java and Python:
// โโโ JAVA BLUEPRINT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
class TimeSlot {
private final long start;
private final long end;
public TimeSlot(long start, long end) {
if (start >= end) {
throw new IllegalArgumentException("Start time must be strictly before end time.");
}
if (start < 0) {
throw new IllegalArgumentException("Time values cannot be negative.");
}
this.start = start;
this.end = end;
}
public long getStart() { return start; }
public long getEnd() { return end; }
public boolean overlaps(TimeSlot other) {
return this.start < other.end && other.start < this.end;
}
@Override
public String toString() {
return "[" + start + " - " + end + "]";
}
}
class Booking {
private final String id;
private final Room room;
private final TimeSlot timeSlot;
private final String organizer;
public Booking(String id, Room room, TimeSlot timeSlot, String organizer) {
this.id = Objects.requireNonNull(id);
this.room = Objects.requireNonNull(room);
this.timeSlot = Objects.requireNonNull(timeSlot);
this.organizer = Objects.requireNonNull(organizer);
}
public String getId() { return id; }
public Room getRoom() { return room; }
public TimeSlot getTimeSlot() { return timeSlot; }
public String getOrganizer() { return organizer; }
}
class Room {
private final String id;
private final int capacity;
private final List<Booking> bookings = new ArrayList<>();
private final ReentrantLock lock = new ReentrantLock();
public Room(String id, int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("Capacity must be positive.");
}
this.id = Objects.requireNonNull(id);
this.capacity = capacity;
}
public String getId() { return id; }
public int getCapacity() { return capacity; }
public boolean isAvailable(TimeSlot slot) {
lock.lock();
try {
for (Booking existing : bookings) {
if (existing.getTimeSlot().overlaps(slot)) {
return false;
}
}
return true;
} finally {
lock.unlock();
}
}
public boolean book(Booking booking) {
lock.lock();
try {
for (Booking existing : bookings) {
if (existing.getTimeSlot().overlaps(booking.getTimeSlot())) {
return false;
}
}
bookings.add(booking);
return true;
} finally {
lock.unlock();
}
}
public List<Booking> getBookings() {
lock.lock();
try {
return new ArrayList<>(bookings);
} finally {
lock.unlock();
}
}
}
interface RoomSelectionStrategy {
Room selectRoom(List<Room> rooms, int attendees, TimeSlot slot);
}
class BestFitRoomSelectionStrategy implements RoomSelectionStrategy {
@Override
public Room selectRoom(List<Room> rooms, int attendees, TimeSlot slot) {
List<Room> candidateRooms = new ArrayList<>();
for (Room room : rooms) {
if (room.getCapacity() >= attendees) {
candidateRooms.add(room);
}
}
candidateRooms.sort(Comparator.comparingInt(Room::getCapacity));
for (Room room : candidateRooms) {
if (room.isAvailable(slot)) {
return room;
}
}
return null;
}
}
class FirstFitRoomSelectionStrategy implements RoomSelectionStrategy {
@Override
public Room selectRoom(List<Room> rooms, int attendees, TimeSlot slot) {
for (Room room : rooms) {
if (room.getCapacity() >= attendees && room.isAvailable(slot)) {
return room;
}
}
return null;
}
}
class MeetingScheduler {
private final List<Room> rooms = new CopyOnWriteArrayList<>();
private final RoomSelectionStrategy roomSelectionStrategy;
public MeetingScheduler(RoomSelectionStrategy strategy) {
this.roomSelectionStrategy = Objects.requireNonNull(strategy);
}
public void addRoom(Room room) {
rooms.add(Objects.requireNonNull(room));
}
public List<Room> findAvailableRooms(TimeSlot slot, int minCapacity) {
List<Room> available = new ArrayList<>();
for (Room room : rooms) {
if (room.getCapacity() >= minCapacity && room.isAvailable(slot)) {
available.add(room);
}
}
return available;
}
public Booking scheduleMeeting(String organizer, int attendees, TimeSlot slot) {
Room room = roomSelectionStrategy.selectRoom(rooms, attendees, slot);
if (room != null) {
String bookingId = "BKG-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
Booking booking = new Booking(bookingId, room, slot, organizer);
if (room.book(booking)) {
System.out.println("[Scheduler] Success: Room " + room.getId() + " booked for " + organizer + " " + slot);
return booking;
}
}
System.out.println("[Scheduler] Fail: No room available for " + organizer + " at slot " + slot + " for " + attendees + " attendees.");
return null;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
MeetingScheduler scheduler = new MeetingScheduler(new BestFitRoomSelectionStrategy());
scheduler.addRoom(new Room("Room-A", 5));
scheduler.addRoom(new Room("Room-B", 10));
scheduler.addRoom(new Room("Room-C", 15));
System.out.println("--- Starting Concurrent Meeting Booking Simulation ---");
ExecutorService executor = Executors.newFixedThreadPool(4);
TimeSlot slot1 = new TimeSlot(10, 12);
TimeSlot slot2 = new TimeSlot(11, 13); // Overlaps with slot1
// Task 1: Book Room for 4 people at 10-12
Runnable task1 = () -> scheduler.scheduleMeeting("Alice", 4, slot1);
// Task 2: Book Room for 8 people at 10-12 (should go to Room-B since Room-A is for 5)
Runnable task2 = () -> scheduler.scheduleMeeting("Bob", 8, slot1);
// Task 3: Book Room for 4 people at 11-13 (overlaps with slot1, should go to Room-C if A is taken)
Runnable task3 = () -> scheduler.scheduleMeeting("Charlie", 4, slot2);
// Task 4: Duplicate Alice's slot attempt by Dave (should fail if Alice gets Room-A first)
Runnable task4 = () -> scheduler.scheduleMeeting("Dave", 4, slot1);
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
executor.submit(task4);
executor.shutdown();
executor.awaitTermination(2, TimeUnit.SECONDS);
System.out.println("--- Booking Simulation Completed ---");
}
}Review
Help Us Improve
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Placeholder
Discussion
Share your thoughts, ask questions, or help others.
Loading comments...