Machine Coding Problem

Hotel Booking System

maco30maco60macoAllcommercestrategy-refund-&-fine-grained-concurrent-booking
Commonly Asked By:Booking.comAirbnbExpedia

Functional Scope (In-Scope)

  • Room Search & Filters: Fetch available room items matching active date ranges and type selections.
  • Overlap Interval Checker: Accurately verify check-in check-out ranges to avoid booking collisions.
  • Concurrent Booking Prevents: Optimistically or pessimistically lock rooms during reservation pipelines using fine-grained locks to prevent double-booking.
  • Structured Cancellation Strategy: Tiered cancellation refund strategies matching flexible vs non-refundable structures.

Explicit Boundaries (Out-of-Scope)

  • No Hardware Access Key Integration: Ignores physical card key printing interfaces or lock hardware.
  • No Dynamic flight/cab Combos: Strictly manages room logs rather than third-party inventory.

Practical reference designs showing booking security in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

enum RoomType { SINGLE, DOUBLE, SUITE }

interface CancellationStrategy {
    double calculateRefund(double paidAmount, long checkInTime, long cancelTime);
}

class FlexibleCancellationStrategy implements CancellationStrategy {
    @Override
    public double calculateRefund(double paidAmount, long checkInTime, long cancelTime) {
        long hoursToCheckIn = (checkInTime - cancelTime) / (1000 * 60 * 60);
        if (hoursToCheckIn >= 48) {
            return paidAmount; // 100% refund
        } else if (hoursToCheckIn >= 24) {
            return paidAmount * 0.5; // 50% refund
        }
        return 0.0; // No refund
    }
}

class NonRefundableCancellationStrategy implements CancellationStrategy {
    @Override
    public double calculateRefund(double paidAmount, long checkInTime, long cancelTime) {
        return 0.0; // Always 0% refund
    }
}

class Room {
    private final String id;
    private final RoomType type;
    private final double basePrice;
    private final ReentrantLock lock = new ReentrantLock();

    public Room(String id, RoomType type, double basePrice) {
        this.id = id;
        this.type = type;
        this.basePrice = basePrice;
    }

    public String getId() { return id; }
    public RoomType getType() { return type; }
    public double getBasePrice() { return basePrice; }
    public ReentrantLock getLock() { return lock; }
}

class Reservation {
    private final String id;
    private final String guestName;
    private final Room room;
    private final long checkIn;
    private final long checkOut;
    private final double cost;
    private volatile boolean cancelled = false;

    public Reservation(String id, String guest, Room room, long in, long out, double cost) {
        this.id = id;
        this.guestName = guest;
        this.room = room;
        this.checkIn = in;
        this.checkOut = out;
        this.cost = cost;
    }

    public String getId() { return id; }
    public Room getRoom() { return room; }
    public long getCheckIn() { return checkIn; }
    public long getCheckOut() { return checkOut; }
    public double getCost() { return cost; }
    public boolean isCancelled() { return cancelled; }
    public void cancel() { this.cancelled = true; }

    public boolean overlaps(long start, long end) {
        return !cancelled && checkIn < end && start < checkOut;
    }
}

class HotelBookingService {
    private final Map<String, Room> rooms = new ConcurrentHashMap<>();
    private final List<Reservation> reservations = new CopyOnWriteArrayList<>();
    private final CancellationStrategy cancellationStrategy;

    public HotelBookingService(CancellationStrategy cancellationStrategy) {
        this.cancellationStrategy = cancellationStrategy;
    }

    public void addRoom(Room r) { rooms.put(r.getId(), r); }

    public List<Room> findAvailableRooms(long checkIn, long checkOut, RoomType type) {
        List<Room> available = new ArrayList<>();
        for (Room room : rooms.values()) {
            if (room.getType() != type) continue;
            boolean isFree = true;
            for (Reservation res : reservations) {
                if (res.getRoom().getId().equals(room.getId()) && res.overlaps(checkIn, checkOut)) {
                    isFree = false;
                    break;
                }
            }
            if (isFree) {
                available.add(room);
            }
        }
        return available;
    }

    public Reservation bookRoom(String guest, String roomId, long checkIn, long checkOut) {
        Room room = rooms.get(roomId);
        if (room == null) return null;

        // Fine-grained lock per room ensures high throughput compared to global locking
        room.getLock().lock();
        try {
            // Double check availability (Check-then-Act)
            for (Reservation res : reservations) {
                if (res.getRoom().getId().equals(roomId) && res.overlaps(checkIn, checkOut)) {
                    System.out.println("[Booking Error] Room " + roomId + " is already booked for these dates!");
                    return null;
                }
            }

            long days = Math.max(1, (checkOut - checkIn) / (1000 * 60 * 60 * 24));
            double totalCost = days * room.getBasePrice();

            String resId = "RES-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
            Reservation reservation = new Reservation(resId, guest, room, checkIn, checkOut, totalCost);
            reservations.add(reservation);
            System.out.println("[Booking Success] Guest " + guest + " booked room " + roomId + " (Cost: $" + totalCost + ")");
            return reservation;
        } finally {
            room.getLock().unlock();
        }
    }

    public double cancelReservation(String reservationId, long cancelTime) {
        for (Reservation res : reservations) {
            if (res.getId().equals(reservationId)) {
                res.getRoom().getLock().lock();
                try {
                    if (res.isCancelled()) {
                        System.out.println("[Cancellation Error] Reservation already cancelled.");
                        return 0.0;
                    }
                    res.cancel();
                    double refund = cancellationStrategy.calculateRefund(res.getCost(), res.getCheckIn(), cancelTime);
                    System.out.println("[Cancellation Success] Refund processed: $" + refund + " for reservation " + reservationId);
                    return refund;
                } finally {
                    res.getRoom().getLock().unlock();
                }
            }
        }
        System.out.println("[Cancellation Error] Reservation not found.");
        return 0.0;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== HOTEL CONCURRENT SIMULATION DRIVER ===");
        CancellationStrategy strategy = new FlexibleCancellationStrategy();
        HotelBookingService hotel = new HotelBookingService(strategy);

        Room r1 = new Room("SUITE-101", RoomType.SUITE, 250.0);
        hotel.addRoom(r1);

        long now = System.currentTimeMillis();
        long in = now + (3 * 24 * 60 * 60 * 1000);  // 3 days from now
        long out = now + (5 * 24 * 60 * 60 * 1000); // 5 days from now

        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        
        // Spawn 2 parallel concurrent booking attempts for the SAME room
        threadPool.submit(() -> hotel.bookRoom("Alice", "SUITE-101", in, out));
        threadPool.submit(() -> hotel.bookRoom("Bob", "SUITE-101", in, out));

        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.SECONDS);
    }
}