Requirements


Functional Requirements:


  • Allow reservation of a parking spot. There should be no race condition for reserviation
  • Process payment for the reservation.
  • Enable parking of a car in the reserved spot.
  • Support early departure before reservation time expires.
  • Gate check-in/out.
  • Handle no show.
  • check capacity and prife


Non-Functional Requirements:


  • List the key non-functional requirements (eg low latency, scalability, reliability, etc.)...
  • The parking lot system should be reliable around the year
  • The payment system should be higly reliable and with low latency
  • Only one reservation is avalible for one car
  • the system should be scalable to manage mulitple parking spaces
  • system should be reliable even if system doesn't connect ot internet
  • The system should be consisitent, preventing the double booking for the same parking spot
  • Strong consistency (no double-booking): This is the defining requirement. Once a spot is reserved for a time window, no other user can reserve the same spot for an overlapping window. A single double-booking destroys user trust.
  • High availability: The system must stay operational during component failures. A cache failure should not prevent bookings, it should just be slower. 


API Design

check_capacity(lot_id, vehicle_type, start_time, end_time): Returns available spot count and price for the requested window. Reads from Redis cache for fast response. If cache is stale or unavailable, falls back to database. The response includes both the count and the price so users can make an informed decision before committing. The response does not reveal which specific spots are available, only the count. This prevents users from racing to specific spots and simplifies the API.

reserve_spot(user_id, lot_id, vehicle_type, start_time, end_time): Finds an available spot, creates a TENTATIVE reservation, and returns the reservation_id plus a payment redirect URL. The spot is held for 15 minutes while the user completes payment. This is the most critical endpoint, it must be atomic, consistent, and handle concurrent access correctly. The response includes the assigned spot number (for the gate display), the payment URL, and the hold expiration time so the client can show a countdown.

complete_reservation(user_id, reservation_id, payment_token): Verifies the payment token with the third-party provider and updates the reservation status to CONFIRMED. If the token is invalid, the reservation stays TENTATIVE until the hold expires. This endpoint does not touch the bitmap, the bitmap was already updated during reserve_spot. It only changes the reservation status.


vehicle_arrived(reservation_id, timestamp): Records check-in time in the Transaction table. Called by the gate hardware when a vehicle enters. The gate validates the reservation exists and has status=CONFIRMED before opening the barrier.

vehicle_left(reservation_id, timestamp): Records check-out time. If the vehicle stayed past the reservation end time, flags the overstay for additional charges. The charge is calculated based on the lot's hourly rate for the overstay duration, rounded up to the nearest 15-minute slot.



High-Level Design

API Gateway: Rate limiting, TLS termination, authentication, and request routing. Routes reservation-related requests to the Reservation Service and gate-related requests to the Transaction Service. Also protects against DDoS attacks that could prevent legitimate users from booking during high-demand events. Rate limiting is per-user for check_capacity (prevent scraping) and per-lot for reserve_spot (prevent inventory hoarding). The gateway also validates request parameters before forwarding, rejecting requests with end_time before start_time or reservation windows exceeding the maximum allowed duration (7 days).

Reservation Service: Handles check_capacity, reserve_spot, and complete_reservation. This is the only service that writes to the Reservation table and updates bitmaps. All writes happen inside PostgreSQL transactions. This single-writer design is intentional, having one service own all bitmap mutations eliminates cross-service coordination and makes concurrency reasoning straightforward.

Transaction Service: Handles vehicle_arrived and vehicle_left. Writes to the Transaction table only. It never modifies reservations or bitmaps. This boundary is enforced at the code level, the Transaction Service has a database connection with write access only to the Transaction table, not the Reservation or Spot tables.

Redis Cache: Stores lot capacity snapshots for fast check_capacity reads. This cache is advisory only, it tells users "roughly how many spots are available" but never authorizes a booking. The database always has the final say.

PostgreSQL: Source of truth for all reservation and transaction data. Sharded by lot_id for data locality. Each shard has a primary and at least one read replica for fault tolerance. The bitmap columns and reservation records for a lot always reside on the same shard, enabling single-shard transactions. Indexes on (lot_id, status, start_time) accelerate the common queries: finding active reservations for a lot, expired tentative holds, and no-show candidates.

Payment Provider: Third-party service (Stripe/PayPal) that handles payment processing. The Reservation Service verifies payment tokens with the provider during complete_reservation. The system stores only the transaction reference, not credit card details, PCI compliance is delegated entirely to the payment provider.

Background Sweepers: Two scheduled jobs. The hold expiration sweeper runs every minute, finding TENTATIVE reservations past their expires_at and releasing their bitmap bits. The no-show monitor runs every 15 minutes, finding CONFIRMED reservations with no check-in after 8 hours. Both clear bitmap bits and update reservation status inside transactions.



Detailed Component Design

Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.

Reservation Flow

  1. User calls check_capacity(lot_id, vehicle_type, start, end).
  2. Reservation Service reads from Redis cache (or DB on cache miss). Returns available spot count and pricing.
  3. User calls reserve_spot(user_id, lot_id, vehicle_type, start, end).
  4. Reservation Service begins a transaction. Executes SELECT FOR UPDATE on candidate spot rows in the requested lot.
  5. For each locked spot, performs bitwise AND between the request bitmap and the spot's existing bitmap. If the result is zero, the spot is free.
  6. For the first free spot: INSERT reservation with status=TENTATIVE, update the bitmap with OR (existing OR request), COMMIT.
  7. Returns reservation_id and payment redirect URL to the user.
  8. User pays via the third-party provider, then calls complete_reservation(user_id, reservation_id, payment_token).
  9. Reservation Service verifies the token with the payment provider. On success: UPDATE reservation SET status=CONFIRMED, payment_status=PAID.

Notice that steps 4-6 are the critical section, they happen inside a single database transaction with row-level locks. Steps 7-9 happen outside the transaction, so the lock is released quickly. This separation is why the bitmap check is fast even under contention: the lock is held only for the microseconds needed to check bitmap, insert, and commit. The payment (which takes seconds) happens after the lock is released.

Gate Flow

  1. Vehicle arrives at the gate. Gate scans the reservation QR code or license plate.
  2. Gate calls vehicle_arrived(reservation_id, timestamp).
  3. Transaction Service creates a transaction record with the check_in_time.
  4. Vehicle departs. Gate calls vehicle_left(reservation_id, timestamp).
  5. Transaction Service updates the check_out_time. If the departure is past the reservation end_time, the overstay is flagged for additional charges. The Reservation Service is notified to process the extra payment against the user's payment method on file.

The gate flow is deliberately simple, the complexity lives in the reservation flow where money and spot allocation happen. The gate is just recording physical events. This means gate hardware can be cheap, stateless devices running minimal firmware. If the Transaction Service is temporarily unreachable, the gate can cache events locally and sync later. A delayed check-in record does not cause a double-booking because the bitmap was already set during reservation.