Requirements
Functional Requirements:
- Parking Availability
- allow user to check which spot is available
- Parking reservation
- allow user to reserve parking spot
- Payment
- allow user to pay the service fee
- Check-In/Check-Out
- allow user to check in when they arrive at the parking lot
- allow user to check out when they leave the parking lot
- No-show handling
- able to transition reserved parking lot back to available if the owner doesn't showup
Non-Functional Requirements:
- Low Latency
- response time < 100 ms
- High Availability
- 99.99 % uptime
- Consistency
- strong consistency must be enforced.
- Highly Scalable
- the system should be able to increase/decrease service capacity based on current metric such as
- incoming throughput
- latency
- cpu utilization
- etc
- supporting estimated load mentioned in Capacity Estimation
- the system should be able to increase/decrease service capacity based on current metric such as
Capacity Estimation
- Assumption
- 100 k parking lots across the country
- 1 million daily active users
- each user send 100 requests/day
- load
- throughput will be: 100,000,000/(60*60*365) = 76.103 ~ 80 requests/second
- Peak estimation 10x: 800 requests/second
API Design
External API
- Create Reservation
- Method: POST
- Endpoint: /{version}/reservations
- Path Variables:
- version: str: api's version
- Body
- parking_lot_id: id: id of the parking lot
- Response
- Success
- status: ENUM: SUCCESS
- status_code: int: 200
- parking_lot_id: id: id of the parking lot id
- The parking lot is already reserved
- status: ENUM: ERROR
- status_code: int: 409
- message: str: this parking lot is no longer available
- Success
- Payment
- Method: POST
- Endpoint: /{version}/payments
- Path Variables
- version: str: version of api
- Body
- payment_method: ENUM: payment method
- payment_detail: object: payment details
- Response
- Success
- status: ENUM: SUCCESS
- status_code: int: 200
- payment_id: id: id of the payment transaction
- Duplicate
- status: ENUM: ERROR
- status_code: int: 409
- message: str: this transaction is already completed
- Success
- Check In/ Check out
- Method: PATCH
- Endpoint: /{version}/reservations/{parking_id}
- Path Variables
- version: str: api's version
- parking_id: id: id of the parking lot
- Body
- status: ENUM: CHECK_IN/CHECK_OUT
- Response
- Success
- status: ENUM: SUCCESS
- status_code: int: 200
- parking_status: ENUM: CHECKED_IN
- Conflict
- status: ENUM: ERROR
- status_code: int: 409
- message: str: this spot is reserved by someone else or is already checked in
- Success
- Check Availability
- Method: GET
- Endpoint: /{version}/reservation?startTime={start}&endTime={end}
- Path Variables
- version: api's version
- startTime: start time
- endTime: end time
- Respone
- Success
- status: SUCCESS
- status_code: 200
- reserved_id: list[id]: list of reserved parking's id
- Success
High-Level Design
Components
- Web UI
- frontend of the service
- API Gateway
- Centralized endpoint for frontend to communicate with the backend
- Payment Service
- Receive payment detail for 3rd party company like bank or credit card company to process transaction
- Notification System
- notification to the cache that the payment is completed
- Reservation Service
- Reservations are created here
- Check In service
- Check in reserved parking spot
- Check out service
- Check out reserved parking spot
- Cache
- contain popular spot to reduce load on database
- DB
- source of truth must enforce strong consistency
- No Show Monitoring
- check every 15 minutes from the start of time slot if the user check it. If there is no check in then remove the record from db
Detailed Component Design
Database design
- data nature
- relationship
- 1 User: Many parking lot
- schema datatic
- relationship
- choices
- noSQL database
- we can use document database to store the data
- document will look like this
- user_id: id: user's id
- username: str: user's name
- parking_lot_reserved: list[object]: list of document of parking lot reserved
- parking lot document will look like this
- parking_lot_id: id: parking lot id
- start_time: timestamp: start time
- end_time: timestamp: end
- SQL database
- we can create 3 tables
- User
- user_id
- username
- Parking lot
- parking_lot_id
- parking_lot_location
- User_Parking
- user_id
- parking_id
- start_time
- end_time
- status
- in this case the sql database is more preferred. As the data has consistent structure. And it the document db's structure is a bit awkward here.
- noSQL database
- In this system we also need to shard databases to support the write transaction (800 rps). The most preferred option is to shard User_Parking Table with parking_id. And we will try to maintain the size each shard to stay the same. For the less popular parking spot it will remain on the same shard. While the more popular shard can have multiple shard.
- We also needed to index the User_Parking Table by parking_id, start_time for quick search as parking_id, start_time is frequently used by service such Reservation Service, Check In service and Check Out Service
- we also delete every data that lived longer than 30 days to save space
- Strict consistency needed to be enforced here. The write will not return Success until every replica have synced the data with the write node
No Show Monitoring Service
- To prevent the user to irresponsibly book the parking spot if the parking spot is not checked in within 15 minute. The reservation will be canceled.
- We can do this by periodically (every 30 minute about xx:15, xx:45) query if the staus = "Reserved" and start_time = curr_time-15
- We removed those reservation from the db
Conflict
- We put queue after the reservation service.
- In case there is demand to book the same spot at the same time queue will prevent the write concurrent
- The later request will be rejected.