System requirements


Functional:

  • Cars should get the ticket when they enter the parking
  • Cars should be entering the parking only when there is enough space and space fits the type of the car
  • Cars should pay at exit by scanning the ticket


Non-Functional:

  • Scalability, system should handle large number of cars
  • Availability, cars shouldn't wait in long lines at the entrances or exits
  • Consistency, when the car gets the ticket, the system should see the most frequent data, otherwise the parking might be overloaded.



Capacity estimation

Max capacity is 100,000.

Let's say that in peak hour we get around 80,000 cars.

10% of lots would be for trucks

25% - for motorcycles

65% - for cars.


Number of exits should be proportional to the number of available parking lots.

If we have 80000 cars in one hour.

One car takes 20 seconds to enter.

80000/60 = 1333 cars arrive per minute

1333 / 3 = 440 cars arrive per 20 seconds


So we need approximately 440 entrances.

For simplicity let's say that for exit we need the same time, so we need 440 exits as well.


API design

POST

parkCar(type) - when the car tries to enter, system tries to park in the system, it gets the type of the car, looks at available slots, if it doesn't have any, returns an error response. Otherwise, returns the ticket.


POST

pay(ticketId, paymentType) - paying the ticket when exiting the parking. Returns receipt.





Database design

Database will be RDB, because we have structural data like parking type, ticket, car, receipt.





High-level design

  • Load balancer to balance the requests among the cluster of application servers
  • We will need a cluster of application servers that handles entrance and exit functionality.
  • Multinode RDB to store all the data.
  • Cache effectively retrieve frequently used data, in case of parking lot it would be useful to get availability of the parking space.
  • Queue should be used in case the parking doesn't have empty space.






Request flows

The car gets to the entrance, tries to get the ticket by providing the car type, the request goes to the application server, which gets the parking availability from the cache, in case there is no entry, gets the data from the DB and updates the cache afterwards.


The same flow is for checking out at the exit, but without using the cache, and checking the parking availability.





Detailed component design

The space in the DB can be divided into car type + number of available slots.


LRU cache eviction policy works well here. But our cache won't be overflowed ever, because we don't need much data in there.

Cache can actually be stored in the application as a structure, because we don't store a lot of data in there.


We can replicate an application server to avoid SPOF. We don't need checkpointing because our server is stateless.


Application server will use the Queue structure to handle the cars that were denied due to space limit. It will use First In First Out.


There is no much need of sharding the DB, due to low amount of data. But sync replication to a couple of nodes will work well to avoid SPOF.


In case the ticket was lost, we will have an entry with the car plate in the system, time of arrival, bill, name of the driver etc.



Trade offs/Tech choices

We could be using a dedicated cache as a separate server, which would provide us decoupling, but it is not worth the effort and cost, as it can the data is small in case of the parking system, and we can easily reload it from the RDB.


Alternative to sync replication in the DB, would be async, which would speed up write requests, but in the parking system we highly value consistency over a little write latency.




Failure scenarios/bottlenecks

In case all our application servers fail, we would lose the cache, as it is co-located with the application servers, but it would be pretty easy to restore it from the DB.


If we get to many cars at once at multiple entries, we will face latency in our system, due to sync replication.





Future improvements

Separate entrance from exit servers to separate the load.