Bird
Raised Fist0

Amazon Locker Service

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Design an Amazon Locker Service
40 min ยท medium
medium

Design a system to manage Amazon Lockers distributed across multiple locations. Each locker has multiple compartments of different sizes. Packages are assigned to lockers based on size and availability. Users receive OTPs to retrieve packages. OTPs expire after a configurable time. The system must handle concurrent package assignments and OTP validations.

  • Lockers have multiple compartments of sizes SMALL, MEDIUM, LARGE.
  • Packages must be assigned to a compartment matching or exceeding package size.
  • OTP is a 6-digit code generated per package assignment and expires after 15 minutes.
  • Multiple threads may assign packages concurrently; no double booking allowed.
  • Locker compartments can be locked/unlocked atomically during package pickup.
  • The system should allow adding new locker locations without modifying existing classes.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Locker and Compartment Classes
Design classes to represent Lockers and their compartments with size and availability state.
๐Ÿ’ก Hint
Use enum for compartment sizes to ensure type safety. Use volatile or synchronized to manage availability state changes.
Use enum CompartmentSize {SMALL, MEDIUM, LARGE} for type safety; LockerCompartment has volatile boolean isAvailable to reflect concurrent access; synchronized methods to atomically check and update availability.
Mid: Uses enum for compartment size. Senior: Explains volatile ensures visibility of isAvailable changes across threads; synchronized methods prevent race conditions during assignment.
โš ๏ธCommon Wrong Answer
Using String for compartment size leads to runtime typo bugs and no compile-time checks; not using volatile or synchronized causes visibility and race conditions leading to double booking.
Step 2
Design Package Assignment Logic
Implement method to assign a package to an available compartment matching size constraints.
๐Ÿ’ก Hint
Use synchronized boolean assignPackage(Package pkg) in LockerCompartment to atomically check isAvailable and mark occupied.
synchronized boolean assignPackage(Package pkg) to atomically check isAvailable and set to false; prevents race conditions where two threads assign the same compartment.
Mid: Uses synchronized method for atomic check-and-set. Senior: Traces thread interleaving where without synchronization, two threads read isAvailable=true and both assign, causing double booking.
โš ๏ธCommon Wrong Answer
Not synchronizing assignPackage leads to race conditions and double booking; using AtomicBoolean without proper locking can still cause inconsistent state if multiple fields are involved.
Step 3
Implement OTP Generation and Expiry
Design OTP generation per package assignment and automatic expiry after 15 minutes.
๐Ÿ’ก Hint
Use a class OTP with final String code and volatile long expiryTimestamp; schedule expiry with ScheduledExecutorService or store expiry time and check on validation.
OTP code is final String; expiryTimestamp is volatile long to ensure visibility; OTP validation checks currentTimeMillis against expiryTimestamp; ScheduledExecutorService can remove expired OTPs asynchronously.
Mid: Uses final for immutable OTP code; volatile expiryTimestamp for visibility. Senior: Explains how volatile ensures threads see updated expiryTimestamp; scheduled tasks prevent stale OTP usage.
โš ๏ธCommon Wrong Answer
Using mutable String without final or volatile expiryTimestamp causes stale reads; no expiry check allows OTP reuse beyond validity.
Step 4
Design Locker Service for Concurrency and Extensibility
Design a LockerService class to manage multiple lockers, package assignments, and OTP validations concurrently.
๐Ÿ’ก Hint
Use ConcurrentHashMap for locker storage; synchronized or ReentrantLock for package assignment per locker; abstract class or interface for Locker to allow extension.
LockerService uses ConcurrentHashMap<String, Locker> for thread-safe locker lookup; synchronized boolean assignPackageToLocker(String lockerId, Package pkg) to serialize assignment per locker; abstract class Locker to hold state and behavior, enabling extension without modification (Open-Closed Principle).
Mid: Uses ConcurrentHashMap and synchronized methods. Senior: Explains how concurrent map prevents concurrent modification exceptions; synchronized assignment prevents double booking; abstract class Locker holds state unlike interface, enabling shared code reuse.
โš ๏ธCommon Wrong Answer
Using HashMap causes ConcurrentModificationException; using interface for Locker without state forces duplication; no synchronization causes race conditions.
Step 5
Handle Package Pickup and Locker Compartment Unlocking
Design method to validate OTP and unlock the assigned compartment atomically.
๐Ÿ’ก Hint
Use synchronized boolean unlockCompartment(String otp) to validate OTP and set compartment availability; volatile boolean isLocked to track lock state.
synchronized boolean unlockCompartment(String otp) atomically validates OTP and sets isLocked=false; isLocked is volatile to ensure visibility; prevents race conditions where multiple threads try to unlock same compartment.
Mid: Uses synchronized method for atomic OTP validation and unlocking. Senior: Explains volatile ensures other threads see updated lock state; synchronized prevents interleaving where OTP is validated twice.
โš ๏ธCommon Wrong Answer
Not synchronizing unlock allows multiple unlock attempts; no volatile on isLocked causes stale reads and inconsistent lock state.
๐Ÿ”Œ
API Design Challenge
Design APIs for locker management, package assignment, OTP generation, validation, and package pickup that support concurrency and extensibility.
  • Use RESTful endpoints with idempotent operations for package assignment and pickup.
  • Design OTP validation API to check expiry and lock state atomically.
  • Consider versioning APIs to allow future extensions without breaking clients.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • enum for compartment sizes to prevent typo bugs
  • volatile booleans for compartment availability and lock state
  • synchronized methods for atomic package assignment and unlocking
  • ConcurrentHashMap for thread-safe locker storage
  • abstract class Locker holding state and behavior for extensibility
Good to Have - 30%
  • ScheduledExecutorService for OTP expiry cleanup
  • final fields for immutable OTP code
  • Open-Closed Principle adherence in Locker design
  • Lifecycle ownership clearly defined (composition vs aggregation)
  • Detailed concurrency scenario explanation
Bonus - 10%
    โœ…
    Model Answer
    โ–ผ Reveal

    This design provides a thread-safe, extensible Amazon Locker Service managing lockers with multiple compartments, package assignments, OTP generation with expiry, and atomic package pickup operations. It uses Java concurrency constructs to prevent race conditions and ensures visibility of state changes.

    • Use enum for compartment sizes to prevent runtime typo bugs and ensure compile-time safety.
    • Use volatile booleans isAvailable and isLocked in LockerCompartment to ensure visibility across threads.
    • Synchronize assignPackage() and unlockCompartment() methods to prevent race conditions and double booking.
    • Use ConcurrentHashMap in LockerService for thread-safe locker storage and retrieval.
    • Define Locker as an abstract class to hold state and behavior, enabling extension without modifying existing code (Open-Closed Principle).
    • Generate OTP as immutable final String with volatile expiryTimestamp; validate expiry on use and clean expired OTPs asynchronously with ScheduledExecutorService.
    • Synchronize package assignment at locker level to serialize access and prevent concurrent double booking.
    • Use volatile and synchronized together to ensure atomicity and visibility in concurrent environment.
    • Use enum CompartmentSize for compartment size safety; prevents runtime typo bugs.
    • LockerCompartment has volatile boolean isAvailable; synchronized assignPackage() for atomic check-and-set.
    • OTP class uses final String code and volatile long expiryTimestamp; scheduled expiry or validation-time check.
    • LockerService uses ConcurrentHashMap<String, Locker> for thread-safe locker management.
    • Locker is abstract class holding state and behavior; interface cannot hold instance state in Java.
    • synchronized methods in LockerService and LockerCompartment prevent race conditions during package assignment and pickup.
    • volatile booleans (isAvailable, isLocked) ensure visibility of state changes across threads.
    • ScheduledExecutorService or similar mechanism to clean expired OTPs asynchronously.
    • Open-Closed Principle enforced by abstract Locker class allowing extension without modification.
    • Lifecycle: LockerService aggregates Lockers; Lockers compose LockerCompartments; Package assigned to LockerCompartment (association).

    Practice

    (1/5)
    1. In designing the Vehicle abstraction for the Ride Booking system, should Vehicle be an abstract class or an interface, and why?
    easy
    A. Abstract class, because Vehicle has common state like licensePlate and shared methods like startEngine().
    B. Interface, because Vehicle only defines behavior without any shared state or implementation.
    C. Interface, because Java interfaces can now have default methods to share code and state.
    D. Abstract class, because interfaces cannot enforce method signatures.

    Solution

    1. Step 1: Analyze Vehicle's shared state and behavior

      Vehicle has common fields (e.g., licensePlate) and methods (e.g., startEngine) that require shared implementation and state storage.
    2. Step 2: Abstract class vs interface suitability

      Abstract classes allow shared state and method implementations, while interfaces cannot hold instance state. Thus, abstract class fits Vehicle better.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Shared state + behavior -> abstract class [check]
    Hint: Shared state means abstract class, not interface [check]
    Common Mistakes:
    2. In the seat booking process, if the bookSeat() method is not synchronized, which exact sequence of events between Thread 1 and Thread 2 causes double-booking of the same seat?
    medium
    A. Thread 1 checks seat availability -> true, context switch before booking; Thread 2 checks availability -> true, both book seat concurrently.
    B. Thread 1 reads seat as available, Thread 2 reads seat as occupied, both proceed to book.
    C. Thread 1 locks seat, Thread 2 waits and retries after release, no double-booking occurs.
    D. Thread 1 and Thread 2 both fail to read seat status due to cache inconsistency.

    Solution

    1. Step 1: Trace Thread 1's check-then-act gap

      Thread 1 reads seat as available but is preempted before marking it booked.
    2. Step 2: Thread 2 also reads seat as available and proceeds to book, causing double-booking.

      Without synchronization, both threads act on stale availability state.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Check-then-act gap without lock -> double-booking [check]
    Hint: Check availability -> context switch -> both book [check]
    Common Mistakes:
    3. In a plugin loader, the loadPlugin() method checks if a plugin is already loaded before loading it. Without synchronizing this method, what exact thread interleaving causes the same plugin to be loaded twice?
    medium
    A. Thread 1 checks isLoaded() -> false, context switch; Thread 2 checks isLoaded() -> false; both proceed to load the plugin.
    B. Thread 1 and Thread 2 both wait on a lock, causing deadlock.
    C. Thread 1 loads plugin, but Thread 2 never sees the updated loaded state due to caching.
    D. Thread 1 and Thread 2 both call loadPlugin() simultaneously and both throw exceptions.

    Solution

    1. Step 1: Trace the check-then-act sequence without synchronization

      Thread 1 calls isLoaded() and sees false, then context switches before loading.
    2. Step 2: Thread 2 also calls isLoaded() and sees false, both threads proceed to load plugin concurrently, causing duplicate loading.

    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Unsynchronized check-then-act allows double loading due to race [check]
    Hint: Check-then-act without sync -> double load race condition [check]
    Common Mistakes:
    4. In a multi-threaded parking lot system, if parkVehicle() is NOT synchronized, which exact sequence causes double-booking of the same spot?
    medium
    A. Thread 1 parks vehicle, Thread 2 parks vehicle after Thread 1 finishes.
    B. Thread 1 checks spot availability -> true, context switch; Thread 2 checks availability -> true; both park simultaneously.
    C. Thread 1 parks vehicle, Thread 2 waits for Thread 1 to release lock before parking.
    D. Thread 1 and Thread 2 both fail to find a spot and throw exceptions.

    Solution

    1. Step 1: Identify the race condition

      Without synchronization, both threads can read spot as available before either marks it occupied.
    2. Step 2: Trace exact interleaving

      Thread 1: isAvailable() -> true, context switch before marking occupied; Thread 2: isAvailable() -> true, both proceed to park, causing double-booking.
    3. Final Answer:

      Option B -> Option B
    4. Quick Check:

      Check-then-act gap without synchronization -> double-booking [check]
    Hint: Check availability -> context switch -> both park -> double-booking [check]
    Common Mistakes:
    5. The Airline Reservation System needs to support ElectricCar vehicles requiring ChargingSpot parking. What is the minimum change to the current Java design to support this without modifying existing classes?
    hard
    A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally.
    B. Modify Vehicle base class to include charging capability and add ChargingSpot as a new subclass of Spot.
    C. Create a new Vehicle hierarchy for electric cars and a separate ParkingLot for charging spots.
    D. Add a boolean isElectric field to Vehicle and conditionally handle charging spots in existing Spot classes.

    Solution

    1. Step 1: Identify extensibility requirements

      Adding ElectricCar and ChargingSpot must not modify existing classes to respect OCP and avoid class explosion.
    2. Step 2: Minimal change approach

      Adding new enum values and creating new subclasses for ElectricCar and ChargingSpot, plus updating compatibility externally, extends functionality without changing existing code.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      New enums + subclasses + external compatibility update -> no existing class change [check]
    Hint: New enums + subclasses + external map -> no existing class edits [check]
    Common Mistakes:
    Recommended

    Ready to prove it?

    Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

    10 questions~5 mineasy hard