← Back to list

Why I Added Redis to My Bus Aggregator Product

When I was working on a bus aggregator product, one of the most important flows was seat booking.

stuti mohindra · 2026-06-24 21:53 · 0 claps · 4.7 min read
#backend-development #redis #booking-systems #architecture #database
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🏛️ · Architecture

Why I Added Redis to My Bus Aggregator Product

When I was working on a bus aggregator product, one of the most important flows was seat booking.

At first, the system looked simple.

A customer searched for buses, selected a seat, and moved towards payment. Once the seat was selected, we had to temporarily hold that seat so another customer could not book the same seat at the same time.

Initially, we were handling this directly through the database.

Every time a seat was selected, held, released, or confirmed, the database was updated. This worked fine in the beginning, but as more operators came onboard and traffic increased, we started seeing the problem more clearly.

The database was being used for everything:

  • Reading operator inventory
  • Checking available seats
  • Holding seats temporarily
  • Updating seat status
  • Confirming bookings
  • Syncing operator data

This created unnecessary pressure on the database, especially for actions that were temporary in nature.

A seat hold is not always a confirmed booking. Many users select a seat, go to the payment page, and then drop off. Some users take time. Some payments fail. Some sessions expire.

So the question was:

Should every temporary seat hold be written directly to the main database?

The answer was no.

The Problem with Using Only the Database

In a bus aggregator, seat availability changes frequently.

Let’s say User A selects seat 12 and moves to payment.

For the next few minutes, seat 12 should not be shown as available to other users. But at the same time, we do not want to permanently mark it as booked unless payment is successful.

Earlier, we were updating the database for every seat hold.

That meant the database had to manage both permanent booking data and temporary booking intent.

This created a few problems.

First, the database had more write load than necessary.

Second, temporary holds needed extra cleanup logic. If the user did not complete payment, we had to release the seat again.

Third, seat availability became harder to reason about because some records represented confirmed bookings while others represented temporary holds.

This is where Redis made sense.

Why Redis Was a Better Fit for Seat Holds

Redis is very useful when you need fast temporary storage.

In our case, a seat hold was not permanent data. It was temporary state.

So instead of immediately writing every seat hold to the main database, we introduced Redis.

When a user selected a seat, we created a seat hold in Redis with a TTL of 20 minutes.

That means the seat was locked for 20 minutes.

If the user completed payment within that time, we confirmed the booking and updated the main database.

If the user did not complete payment, the Redis key expired automatically, and the seat became available again.

This made the flow much cleaner.

The database stored confirmed and important long-term booking information.

Redis handled temporary seat locks.

Example Flow

The flow became something like this:

  1. User searches for buses.
  2. User selects a seat.
  3. System checks if the seat is already held or booked.
  4. If available, Redis creates a temporary lock for that seat.
  5. The lock has a TTL of 20 minutes.
  6. User proceeds to payment.
  7. If payment succeeds, the booking is confirmed in the database.
  8. If payment fails or the user drops off, the Redis lock expires automatically.

This reduced unnecessary database writes and simplified cleanup.

The important idea was this:

Redis became the source of truth for temporary seat holds. The database remained the source of truth for confirmed bookings.

Why TTL Was Important

TTL was the key feature that made Redis useful in this case.

Without TTL, we would still need a background job to clean up expired seat holds.

With Redis, the expiry was automatic.

A 20-minute TTL gave the user enough time to complete the payment, but it also prevented seats from being blocked forever.

This is a common pattern in booking systems.

You do not want to permanently reserve inventory just because someone clicked on it. You only want to hold it temporarily while the user is actively completing the transaction.

Separating Reads and Writes

Another improvement we made was separating read and write responsibilities.

In an aggregator product, operator data can come from many different sources. Some operators may have real-time APIs. Some may update data less frequently. Some may have delays or inconsistencies.

We had a read database that was optimized for showing operator inventory to users.

The write side handled updates, bookings, and synchronization.

This separation helped because the search and listing experience required fast reads, while booking and sync operations required correctness.

Instead of making every user-facing search depend directly on operator systems, we maintained our own read-optimized view of operator data.

Handling the Sync Gap

One challenge with aggregator systems is that your data may not always be perfectly real time.

There can be a gap between what the operator system has and what your system shows.

To manage this, we had a refresh process that synced operator data every 24 hours.

This helped keep the read database updated with operator-side changes.

But for seat holds and active bookings, we needed something faster and more temporary. That is why Redis was introduced in the booking flow.

The 24-hour sync handled broader operator inventory updates.

Redis handled short-lived user actions like seat holds.

The database handled confirmed bookings.

Each layer had a clear responsibility.

What This Improved

Adding Redis improved the system in several ways.

It reduced unnecessary writes to the main database.

It made temporary seat holds easier to manage.

It helped prevent double booking during the payment window.

It removed the need for complex cleanup logic for abandoned seat selections.

It also made the architecture easier to understand because temporary and permanent states were separated.

Before Redis, the database had to do too much.

After Redis, the system became more focused:

  • Redis for temporary locks
  • Database for confirmed bookings
  • Read database for operator inventory
  • Sync process for refreshing operator data

The Bigger Lesson

The biggest lesson for me was that not every piece of state belongs in the main database.

Some data is permanent.

Some data is temporary.

Some data is read-heavy.

Some data is write-heavy.

A good architecture does not treat all of these the same way.

In our case, a seat hold was temporary. It needed speed, expiry, and simple cleanup. Redis was a natural fit.

The database was still important, but it no longer had to manage every temporary action in the booking journey.

That made the product more scalable and more reliable.

Final Thoughts

Adding Redis was not about using a trendy technology.

It solved a real product problem.

In a bus aggregator, seat availability is a sensitive flow. Users expect seats to be held when they select them, but the business cannot afford to block seats permanently for users who may never complete payment.

Redis helped us create a better balance.

It gave us fast temporary locking, automatic expiry, and reduced database load.

For me, this was a clear example of how choosing the right tool for the right type of data can make the whole system simpler.


메타데이터
post_id
5e8fc79dbcf5
slug
why-i-added-redis-to-my-bus-aggregator-product-5e8fc79dbcf5
url
https://medium.com/@stuti.mohindra/why-i-added-redis-to-my-bus-aggregator-product-5e8fc79dbcf5
canonical_url
https://medium.com/@stuti.mohindra/why-i-added-redis-to-my-bus-aggregator-product-5e8fc79dbcf5
author_url
https://medium.com/@stuti.mohindra
status
ok
fetched_at
2026-07-14 09:14:07