Machine Coding Problem

Meeting Scheduler

maco30maco60macoAllproductivityconcurrencyinterval-trees
Commonly Asked By:MicrosoftGoogleUberGoldman Sachs

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).

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 ---");
    }
}