Estimate the scale of the system. Consider daily active users, read/write Since we're supporting 10,000tps, we can expect a good 70% would be for search lots and slots. so for reservations probably 3000tps.
For total number of reservations, if we get 3000 per second, a day we get 3000*10^4 = 3*10^7=30M records, assuming each record 3000 bytes, total database size = 90GB, so we don't need to shard this database.
POST /users (user details)
POST /cars (car details)
GET /lots?lat&long
POST /reserve/:spotId (lotId, carId, payment_info) -> reservationId
GET /reservations/:reservationId
PUT /reservations/:reservationId (checkoutTime)
client calls POST apis to create their user account
then they create a car
And they call GET /lots with their location to see nearby lots.
They select a lot and are able to see available spots (or we could just show the number of spots available in the lot (business decision)
They select a lot/spot and call reserve to book a slot in their spot with start and end time and payment info
Once payment is done, reservations service creates an entry for that reservation in the db, we give the user a qr code with reservation id to scan at the parking lot.
When the user reaches the parking, they scan the qtr code at the entry callbox which calls the GET /reservations/:reservationId to get the details, if current time is between start time and grace time before no show (30 mins), we open the gate for the user and mark the reservation as active.
If it is startTime+30 minutes, we show the user an error and call PUT to mark the reservation as no show.
When the user leaves with their car, we either scan the qr code again and the callbox calls the PUT api for reservations to mark the checkout time in the db.
A no show crun job runs everyday and searches the database for reservations on that day and if their status is BOOKED, it marks them as NO_SHOW.
table - cars
carId
make,
model,
license
...
table - reservations
reservationId
carId
startTIme,
endTime,
checkInTime,
checkoutTime
...
For scaling, reservations service has multiple hosts with autoscaling. The load balancer managed requests, since it doesn't matter which server they connect to, we can just do the one with the least connections or least response times. Payment service i'm assuming to be a black box since that is its own design.
For ensuring reservation consistency, since we have high throughput and low chance of collisions, we can use optimistic locking on the slots table. When we are making a reservation, we use a transaction on postgres to make sure reservation and slots status is updated as an atomic operation and the slots table has an rvn column. and the operation updates the row with rvn+1. The table would have a unique constraint on slotId+rvn columns, so if another write comes at the same time for the same slot, it would be rejected.