Asynchronous activation of gift cards: the continuation of the story

After the warehouse was automated, the activity on the site increased. On peak days, the number of activations reached tens of thousands per day. Synchronous activation started to fail: delays, duplicate requests, and data errors. We switched activation to asynchronous processing via RabbitMQ and Laravel Jobs, increased the number of workers, and connected WebSockets for instant balance updates. The load has decreased, duplicates have disappeared, and the user sees the result immediately.

Asynchronous activation of gift cards: the continuation of the story

A Reminder: How It All Started

In our previous article, we shared how we automated a gift card warehouse and eliminated manual work. We replaced Excel with an admin panel, introduced manager-free activation, implemented reporting, and integrated an API with 1C.

At the time, it seemed like the problem was solved. But this story had a sequel, and it's about what happens when automation starts working too well.

Growing Activity: A Problem We Didn't Anticipate

After launching the automation, website activity began to increase. Customers were actively using the gift card activation feature, which was exactly what we had hoped for.

However, during peak periods, especially holidays, the number of activations reached tens of thousands per day.

And that's when the downside of success became apparent.

How Activation Worked: The Synchronous Approach

Initially, the activation process was synchronous:

  1. The user enters a gift card code.

  2. A request is sent to the backend.

  3. The activation business logic is executed, including database queries.

  4. The result is returned to the client.

This approach worked perfectly well with small volumes. However, when a large number of requests arrived simultaneously, problems started to emerge:

  • Delays — caused by limited system capacity and the inability to process synchronous operations efficiently in parallel.

  • Duplicate requests — during peak periods, clients sometimes sent two identical requests, and both were processed successfully. This led to data inconsistencies and errors.

The worst part about duplicate requests was that users could receive double credits, while the system ended up with inconsistent data. This was no longer just a performance issue — it was an actual incident.

The Solution: Asynchronous Processing with RabbitMQ and Laravel Jobs

We decided to completely rethink the activation process. All operations that were causing performance bottlenecks were moved to asynchronous processing.

Here's what we used:

  • RabbitMQ — a message broker for managing tasks and queues.

  • Laravel Jobs — Laravel's built-in mechanism for handling background jobs.

How the user experience changed:

  1. The user enters a gift card code.

  2. The system performs validation, checking the code format, expiration date, and other requirements.

  3. If validation succeeds, the user sees the message: "Your activation request has been accepted. Please wait for the funds to be credited."

  4. The task is added to a queue, where it is processed asynchronously.

This immediately reduced the load on the synchronous request path and addressed the duplicate request problem.

A New Challenge: One Worker Wasn't Enough

Initially, asynchronous processing ran with a single worker. This was enough to ensure system stability, but not to achieve the speed we wanted.

Users had to wait longer than expected for their funds to arrive. One worker simply wasn't enough to deliver an almost instant activation experience.

Here's what we did:

  • Increased the number of workers processing activation requests.

  • Eliminated processing delays, allowing activations to be handled quickly, even during peak periods.

Another Problem: Users Couldn't See Their Balance in Real Time

Asynchronous processing solved the performance and duplicate request issues, but introduced a new user experience challenge.

Before: users activated a gift card and immediately saw their updated balance.

After: users saw a message saying "Please wait for your funds to be credited" and had no idea exactly when the money would appear in their account.

This was frustrating for users. Waiting without feedback is always a worse experience than receiving an immediate result, even if the actual processing takes only a few seconds.

The Solution: WebSockets for Instant Balance Updates

We integrated WebSockets, a technology that allows the server to send data to the client in real time without requiring a page refresh.

Here's how it works:

  1. The user submits an activation request.

  2. The task is added to the queue.

  3. As soon as a worker processes the activation, the server broadcasts an event via WebSocket.

  4. The balance displayed in the website header updates automatically, without reloading the page.

The user sees "Activation accepted" and, a second later, their balance changes.

The experience feels instant again, even though the underlying process remains asynchronous.

The Results: What Changed

The main outcome: we reduced the load on the website, eliminated activation delays for employees, resolved the issue with duplicate requests, and preserved the feeling of an instant result for users.

Technology Stack

  • Backend: Laravel (PHP)

  • Queues: RabbitMQ + Laravel Jobs

  • Real-time communication: WebSockets

  • Database: MySQL

What We Learned from This Experience

1. Successful Automation Isn't the Finish Line — It's a New Beginning

Increased activity is a good thing. But growth also brings challenges that simply don't exist at smaller scales. It's important to be prepared for them.

2. Synchronous Processing Can Become a Bottleneck Under Peak Loads

Anything that can be processed asynchronously should be. It helps reduce system load and prevents an entire class of errors, such as those caused by duplicate requests.

3. Asynchronous Processing Without Feedback Is Bad UX

Users don't care how the system works behind the scenes. What matters to them is seeing the result.

WebSockets are a straightforward way to bring back the feeling of instant feedback.

4. Scaling Is an Iterative Process

First, one worker. Then, several.

First, synchronous processing. Then, asynchronous.

First, no real-time updates. Then, WebSockets.

There's no need to build a perfect system from day one. Instead, identify and solve problems as they arise.

Final Thoughts

This story is about how successful automation often leads to growth, and growth brings new challenges.

We went from synchronous to asynchronous activation, from a single worker to multiple workers, and from a simple "please wait" message to instant balance updates.

And this is most likely not the end of the story.

That's simply how maintaining and developing a product works: you don't just build it and forget about it. You keep improving it and solving new challenges as the product grows.

Let’s talk

Tell us about the task — we’ll estimate it in a couple of days

Contacts