System requirements


Functional:

For users

  1. Make/Cancel a reservation for a parking lot at a time
  2. Check the availability of the parking lot
  3. Make the payments - support Wechat payments, Alipay
  4. Check the reservation history

For admin

  1. Check the availability of the parking lot
  2. Manage the parking lot - Add or delete
  3. Choose the type of the parking lot - For example, we can differenciate the parking for disabilities and normal
  4. Check the Parking Lot Status: Display things like occupancy percentage, estimated wait time, or even a map view of the parking lot


Non-Functional:

  1. Highly available, technically we want to make the system highly available, it might be ok if the system went down and user can still go to the parking lot and park the car. However, admin could not view and validate the parking, also the system includes payment so we should always make it highly available
  2. Highly scalable, we'd like to make sure our system can be scaled if the volume is going high, so we should make our application state-less and highly scalable.
  3. Highly performant, the performance is very important as users dont want to stay there and wait for the payment to be completed
  4. Highly reliable, we must make the payment process highly reliable to not lose any money


Capacity estimation

  1. How many parking lots do we have? 500
  2. What are the volume of reservation requests? We can think of the volume of entry and exit requests, but it would be less as car always doing it one by one. 10 requests per second




API design

Define what APIs are expected from the system...

1.Make a reservation

url: /user/parkinglot/booking

Parameters:

    1. UserId
    2. Time of parking
    3. Type of the parking lot (n - normal, d - disabilities, etc.)

reqType: Post

returns: ReservationId, success or fail

2.Cancel a reservation

url: /user/parkinglot/cancel

Parameters:

    1. UserId
    2. Reservation id

reqType: Post

returns: success or fail, reason if failed

3.View the parking lot

url: /view/parkinglots

reqType: Get

returns: List of parking lots with the status of availability, type of the parking lot, occupancy percentage and estimated wait time

4.Entry the parking lot

url: /user/parkinglot/parking

reqType: Post

Parameters:

    1. UserId
    2. Entry time

returns: parkingId, success or fail

5.Exit the parking lot

url: /user/parkinglot/exit

reqType: Post

Parameters:

    1. UserId
    2. ParkingId

returns: success or fail, reason if failed

6. Make the payment

url: /user/parkinglot/pay

reqType: Post

Parameters:

    1. UserId
    2. Reservation id(null - if entry the parking lot without a reservation, id - for the reservation)
    3. ParkingId (indicates which parking process you are going to pay)
    4. Money amount

returns: success or fail, reason if failed



Database design

I would prefer relational database to store the data for the parking lot instead of non-relational database because of below reasons:

  1. We can easily link the userId with their parking process, wallet, history payments and so on
  2. For payments system we should make the entire process atomic, which means we should keep the payment process to be succeed all together or failed together, no intermidary status. Deducted the money if succeed and rollback the request if failed
  3. The entire volume would not be very high even with the reservation functionalities, as the available parking lots is fixed.





High-level design

We have three load balancers for three service clusters: Parking service, Reservation service and Payment service, and they all connected to the shared Redis cache to speed up the read process and reduce the pressure of the database, and if any cache miss happens the service will make the request directly from the database.






Request flows

Reservation flow

Client log in the system -> view the parking lots and select the available one -> request will be sent to the load balancer -> request distributed to one of the application service -> process the request and persist the data into database, and update the Redis cache -> return to the front-end


Parking flow

Client entry the parking lot -> identified by the system and request will be sent to the load balancer -> request distributed to one of the application service -> process the request and persist the data into database and update the Redis cache -> return to the front-end


Payment flow

  • Pay for the reservered parking

Client log in the system -> check the reservation history -> select the latest one and make the payments -> request sent to load balancer and distributed to the application service -> service called the Wechat or the Alipay based on what user selected -> confirm the payments by the callback of the payment process and update the reservation to be paid/completed.

Pay for the additional fees (Or no reservations)

If user have parked more than the time he reserved or pay directly in the exit, the flow would be

Client scan the QR code -> request sent to load balancer and distributed to the application service -> service called the Wechat or the Alipay based on what user selected -> confirm the payments by the callback of the payment process and update the reservation to be paid/completed.

Pay for the additional fees (Or no reservations)



Detailed component design

Table design for reservation:

UserId, reservationId, reservation start time, reservation end time, parkinglot type

Table design for parking service:

UserId, parkingId, start time, end time

Table design for the parking lot:

CarType, Disabilities (Y/N), isAvailable (Y/N)





Trade offs/Tech choices

1.Pessimistic locking/Optimistic locking - resolving the concurrency issue of the reservation service. Solution: Pessimistic locking need to be using the line-lock via the sql of select for update and it's managed by database itself, whereas Optimistic locking we should maintain a version in that row and check the version first, and take it to make the reservation, and only persist the data if version matches.


2.Relational or non-relational database - we select Relational database for this design

As I mentioned the volume of our system depends on the count of the lots, and generally won't be high. More importantly, we should guarantee the transaction security for the payments to not lose any money for our user, so Relational database is the best choice here.







Failure scenarios/bottlenecks

1.When we make the payments for the current parking process, and we have sent the request to the external service(Wechat or Alipay), what should we do if we did not get any response?


For the overall transactions we have to ensure it's atomic,

  1. if no response got from the external service due to network error, we should fail the request and return proper message to the client
  2. If the request has been sent and network is good and still did not get any response within timeout, we can retry for 3 times to see if we can get the response, if still no luck we can return the failed message and stop persisting to the database.

2.The reservation service may encounter the concurrency issues when multiple people selected the same lot and make the requests, how to resolve it?


If A made the request for the reservation of lot a and B about to completed the reservation, A might not know this lot has been allocated by B, in this case our reservation service should use 'select...for update' to lock the row so no other transaction can modify it unit the current transaction is complete, so the lotId should be the contraint of that table.




Future improvements

We can build a analytic service to keep tracking the metrics of our service via promethius and Grafana, such as JVM metrics like memory usage and cpu usage, to visualize it in Grafana dashboard