Build a Parking Lot System
Difficulty: Medium · Category: Parking System · Languages: Java 17, Python 3.11, Go 1.22, Node.js 20, TypeScript 5.x
Parking Lot System
Background
A parking lot is one of the most classic Low-Level Design problems. It tests your ability to model real-world entities (floors, spots, vehicles, tickets), handle concurrent access, compute duration-based fees, and maintain accurate occupancy statistics.
Production parking systems must handle thousands of vehicles per day, track every entry and exit, support multiple vehicle sizes, and provide real-time availability — all while keeping data consistent under concurrent operations.
Recommended infrastructure: PostgreSQL — gives you relational > integrity for spot allocation, transactional consistency for > concurrent park/unpark, and indexed queries for history lookups.
How It Works
The platform injects your chosen infrastructure SDK(s) via constructor. For example, if you select PostgreSQL:
public class MyParkingLot implements ParkingLotContract {
private final PostgresClient pg;
public MyParkingLot(PostgresClient pg) {
this.pg = pg;
}
}
See the SDK Docs tab for the full API reference of your chosen system.
Your Task
Implement the ParkingLotContract interface. Your implementation must:
- Persist all state externally — spots, tickets, fee rates, and history must be stored in your chosen system (no in-process-only data structures).
- Support three vehicle types: CAR (1 spot), MOTORCYCLE (1 spot), and TRUCK (2 consecutive spots).
- Issue a unique ticket ID on every successful park operation.
- Calculate duration-based fees:
ceil(hours) × hourlyRate. - Be thread-safe: concurrent park/unpark must not double-book spots.
- Track parking history per vehicle number.
- Provide real-time occupancy statistics including per-type breakdowns.
Interface
public interface ParkingLotContract {
void initialize(int floors, int spotsPerFloor);
String parkVehicle(String vehicleNumber, String vehicleType);
double unparkVehicle(String ticketId);
Map<String, Object> getVehicleLocation(String ticketId);
int getAvailableSpots(int floor, String vehicleType);
int getTotalAvailableSpots();
boolean isFull(String vehicleType);
double getFeeRate(String vehicleType);
void setFeeRate(String vehicleType, double ratePerHour);
List<Map<String, Object>> getVehicleHistory(String vehicleNumber);
Map<String, Object> getLotStats();
}
Method Details
| Method | Description |
|---|---|
initialize | Create the lot with N floors × M spots. Clears any existing data. |
parkVehicle | Allocate spot(s) on the lowest available floor. Return ticket ID or null if full. |
unparkVehicle | Free the spot(s), compute fee based on duration × rate. Return fee or -1 for invalid ticket. |
getVehicleLocation | Return {floor, spotNumber, vehicleType} for a parked vehicle. null if unparked/invalid. |
getAvailableSpots | Count of available spots (or consecutive pairs for TRUCK) on a floor. |
getTotalAvailableSpots | Total free individual spots across all floors. |
isFull | true if no spot can accommodate the given vehicle type. |
getFeeRate / setFeeRate | Get or set the hourly rate for a vehicle type. |
getVehicleHistory | Full entry/exit history for a vehicle number, including fees. |
getLotStats | Overall stats: totalSpots, occupiedSpots, occupancyPercent, byType breakdown. |
Constraints
vehicleNumberandvehicleTypeare non-null, non-empty stringsvehicleTypeis one of:"CAR","MOTORCYCLE","TRUCK"floors≥ 1,spotsPerFloor≥ 1ratePerHour≥ 0- Ticket IDs must be unique across the lifetime of the lot
- TRUCK requires 2 consecutive free spots on the same floor
- Performance target: ≥ 5 000 ops/sec with p99 latency ≤ 10 ms
Examples
initialize(3, 10) // 3 floors, 10 spots each = 30 total
setFeeRate("CAR", 20.0)
setFeeRate("MOTORCYCLE", 10.0)
setFeeRate("TRUCK", 40.0)
parkVehicle("KA-01-1234", "CAR") → "TKT-abc123"
parkVehicle("KA-02-5678", "TRUCK") → "TKT-def456"
getVehicleLocation("TKT-abc123") → {floor=1, spotNumber=1, vehicleType="CAR"}
getAvailableSpots(1, "CAR") → 7 (1 car + 1 truck taking 2 spots)
getTotalAvailableSpots() → 27
isFull("CAR") → false
getLotStats() → {totalSpots=30, occupiedSpots=3, occupancyPercent=10.0, byType={...}}
// After 2 hours...
unparkVehicle("TKT-abc123") → 40.0 (2 hours × $20/hr)
unparkVehicle("TKT-abc123") → -1 (already unparked)
getVehicleHistory("KA-01-1234") → [{ticketId="TKT-abc123", floor=1, ...}]
Hints
- Use row-level locking or an equivalent concurrency control when allocating spots to prevent double-booking.
- Generate ticket IDs with
UUID.randomUUID()for uniqueness. - For TRUCK parking, find two consecutive unoccupied spots on the same floor, ordered by spot number, then check for adjacent pairs.
- Compute fee:
Math.ceil((exitTime - entryTime) / 3_600_000.0) * ratePerHour. - Index your spot table on
(floor, is_occupied)for fast availability queries. - Index your ticket table on
vehicle_numberfor history lookups. - Optional schema reference — a relational approach might use tables like
parking_spots(floor, spot_number, is_occupied, ticket_id),parking_tickets(ticket_id, vehicle_number, vehicle_type, floor, spot_number, entry_time, exit_time, fee, is_active), andfee_rates(vehicle_type, rate_per_hour).
Infrastructure your code runs against
- PostgreSQL 16
How your solution is graded
Your score combines functional correctness (40%), performance (40%) and code quality (20%).
- Target throughput: 5,000 operations/second
- Target p99 latency: 10 ms
- Load test concurrency: 8 concurrent clients
- Load test duration: 10 seconds
- Maximum error rate: 0.1%