The amount of people may vary on the each of the parking lots, but assuming we are taking care of all of the parking lots at once, assuming 100 parking lots with 30 spaces, and 5 different people in a slot per day, I would expect at least 100 * 30 * 5 = 45000 people per day at least, with request for parking + checking out (may need to see how), we are dealing with at least 100k requests per day per region. Assuming we are working with new parking / new members, about 100 bytes per new member and 10 bytes per parking, we are working with at least 100k * 10 + 100bytes * 45k = ~ 1.5million bytes -> 1,500,000 1.5Tb per day of transactions, so we would need to get some type of cloud storage.
Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
POST /park {body contains User info + start time + expiration} - reserve Parking lot / check if parking is available / reserve the slot if not taken yet.
POST /pay {body contains User info + payment info if not in user} - process payment
PUT /park {parking_id + new time} - update time on the parking.
PUT /park/early/{park_id} - Early termination of the parking. Open up parking slot.
POST /gate/park_id - allows user to get out of the gate. The expiration time will be 30~1hour (depends on config) after the parking expire time.
PUT /park/no-show/{park_id} - Either leave it empty, or if user not checked in, open up parking slot. Refund depends on the config.
Each of the services, Login service, ParkingService, reserveService, and Gateservices will be in their respectivee containers. The loginservice will focus on user info / logins, reserveService will focus on the actual reservation of the Parking, and the ParkingService will focus on the availability of the parking spot., and the GateService will focus on the Gate openings. Note that the normal client's request on logging in / reserving a parking spot will go to the reserveService / LoginService, and the Gate hardware, it'll send request to the GateService bidirectionally. The ParkingService, which manages which parking spots are available, will communicate with the reserveService.
The database will store data like: User Information / Payment, Parking information, and the payment, as well as the Gate information. The Gate information.
Now let's go one by one.
When the user first reserves a parking lot, We'll call upon the reserveService. The reserveService will first reach out to the Cache. This Cache will be responsible for keeping the information for the parking spots that are currently taken. This is to ensure that there are no 2 same parking reservations that has the same parking_id. Since the user is making a reservation for the parking slot that is most likely not taken, and that reservation isn't in the cache, we would reach out to the Database for the parking information, and we would save this reservation onto the Cache itself. Now the cache should store those information until the expiration of the parking reservation + 1 hour, this 1 hour comes from how long user can have after their expired time to leave the gate. Similarly, the GateService will also utilize the Cache. When the customer is at the gate scanning a QR code to leave (most likely), the gateService will reach out to the Cache to retrieve the correct parking information. Once the parking session is verified, the gate will open, and the parking reservation will change its status to "expired". We will most likely keep the parking data for about a week, just in case something happens tot he parking instead of just deleting the reservation.
For the actual data that we are storing in the database, We would most likely shard the database based on the location of the parking spots. For example, we would have separate database for SF and LA parking lots. If we are utilizing distributed system, we would have this system on each of the kubernetes cluster for each region.
For the Payment system, I'm thinking of utilizing the third-party payment system like Stripe. It's a tradeoff between technical overhead on creating our own payment system, and the cost of using the 3rd party system.
I would choose SQL, since we aren't dealing with changing data models, our data is pretty strict.
Since we are dealing with a lot of reads and writes, I would work with PostgreSQL.
I would probably partition the database based on the location of each parking lots, like Database in LA would be different from database in SF, we would split the database based on the region.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
For the scaling, I would utilize event-driven scaling for each of the services. We would have a separate prometheus pod or other observability tool to get how many requests each pods are getting, and based on resource (cpu / memory utilization), or latency, we would scale up the # of the service containers, approximately around 70% utilization of resource.
When the user is reserving parking slots, we would need to make sure that the parking itself isn't taken already. In order ensure this, we would keep the parking_id / location_id / lot_id as a unique, when this parking is currently active. we would have some boolean on the parking to account for this. Of course, We would need to put a distributed lock on the parking lots themselves so that other reservation would not take it.
for the payment system, I would utilize 3rd party like stripe, the tradeoff here is less work for us/ technical overhead vs more cost.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.