I tried building a Deployable Ticket Booking System
Highlights
- Microservices architecture
- TTL based locking using Redis
- Docker and CI/CD practices
Introduction
I initially assumed the database would handle everything, but I learned how Redis would come into play and in the way I also grasped serious knowledge of setting up backend systems in a microservices driven architecture as well as containerizing such applications using Docker. Eventually, I also learned using Github actions for implementing Continuous Integration (CI/CD practices).
Designing a ticket booking system is not trivial as it involves managing concurrency and tackling race conditions while selecting the seats. The problem is preventing the same seat from being booked twice under load.
The very first instinct for me was to simply follow a CRUD based approach towards booking the tickets, but then I thought of following a rather industrial approach to learn more of building production grade applications.
I set up a microservices based architecture for the project creating separate services for authentication, seat availability and actions, booking, payment ( I have not yet integrated a payment platform). I searched the internet for references and came across various articles in medium and gfg regarding the same, I have mentioned them below. Some really helpful concepts that I learned along the way are :
Locking
Locking ensures that any user temporarily owns the seat while making the decision of either booking it or cancelling it to avoid race conditions among multiple users. Database locks can block other requests and do not scale well. I used a two layer safety mechanism using Redis backed by MongoDB as the persistent storage.
Each seat has it’s own Redis key.
const locked = await redis.set(
lockKey,
userId,
"NX",
"EX",
600
);“NX” -> set the key only if the key does not exist (avoids duplication)
“EX”,600 -> auto expire the lock after 600 seconds
Get Bikram Yadav’s stories in your inbox
Join Medium for free to get updates from this writer.
userId -> the owner of the lock
This step protects the database from unnecessary load as if Redis rejects the above process, we immediately exit without saving to the database.
The seat can be locked either if it is available or if it was locked but the lock has expired. If database update fails, the key is deleted from Redis as well.
if (!seat) {
await redis.del(lockKey);
return res.status(400).json({
message: 'seat cannot be locked'
})
}As because I have set auto expiry of the Redis lock at 600 secs, the locking supports automatic recovery via TTL.
Thus, I found that Redis not only is a cache but acts as a distributed lock manager as well.
Deployment
I added health checks to ensure the service was alive and check when it failed. This step is designed to integrate with Observability later using Prometheus/Grafana . But it just returns a json object for now, when the “/health” endpoint is hit from the gateway.
Local environments differ for different machines and servers differ as well in a production environment. So, to get a setup where it worked seamlessly everywhere, I dockerized the application. I packaged each service and then containerized as a whole.
Manual deployments do not scale, so I added CI to move safely ensuring each service was up, while I simply pushed code to Github. I used Github Actions to achieve this. It acts as an early warning system.
Flow of the application
What this system lacks ?
The system is far from perfect as of now. I note Redis as a single point of failure, as without locking there is no application of booking the seats. I also haven’t handled the case of partial failures yet in the project and also look forward to integrating other dev ops practices into the project such as Observability using Grafana and maybe someday using Kubernetes to maintain the docker containers. But I look forward to adopting an event driven systems architecture using Kafka instead in the future.
The project none the less taught me a lot about how distributed systems work and how do they communicate with each other and also how to dockerize them as well inside a single container. And it also made me familiar with using Redis alongside MongoDB. Looking forward to grow more as a backend and dev-ops engineer.
Here are some important links I studied from while making this project and the github repo attached :






