How We Redesigned Our Background Job Architecture for Faster Bundle Processing

A flash sale on one store was silently delaying inventory sync for every other merchant. Here's the queue design fix that made processing fair.

AI Product Bundle Builder

5 min Read

The complaint wasn't about a bug. No orders were lost, no inventory was corrupted. But a merchant kept telling us their inventory sync felt slow, and they couldn't understand why. Their store wasn't busy. They'd get one or two orders, and the stock update would take longer than it should.

We looked at their store in isolation and nothing was wrong. Then we looked at what was happening across all stores at the same time, and the problem was immediately obvious.

Diagram showing one high-volume store flooding a shared Sidekiq queue and delaying jobs for low-volume stores See full designer spec in the IMAGE SPECS table at the bottom of this file.

Every order triggers several background jobs

When a merchant's store receives an order that includes a bundle, our app doesn't process it inline. It queues background jobs to handle the work asynchronously.

Those jobs cover:

  • Processing the bundle order

  • Updating component inventory

  • Syncing stock across bundled products

  • Recording bundle analytics

  • Triggering any follow-up operations

That's the right approach. Doing this synchronously during checkout would be slow and fragile. Background jobs are the standard pattern.

The problem wasn't the pattern. It was how we organized the queue.

Single queue for all the merchants was the wrong call

When we first built this, all background jobs went into a single shared Sidekiq queue. Every store, every order, every job type, all in one line.

At low volume, it was fine. Jobs processed quickly and nobody noticed any ordering effects.

As the merchant base grew and order volumes increased, the single queue became a fairness problem.

Here's what was actually happening. Say four stores are all using the app on the same day:

  • Store A runs a flash sale and places 1,000 orders in two hours

  • Stores B, C, and D are quiet, each getting a handful of orders

Store A's 1,000 jobs flood the queue. Any job from Store B, C, or D that arrives after the flood starts has to wait behind all of them. A merchant with one order to process ends up waiting for another merchant's entire flash sale to clear.

From the outside, it looks like slow inventory sync. But their store isn't slow. They're just waiting in the wrong line.

We were already using Sidekiq throttling to cap concurrency, but throttling doesn't fix this. It limits how many jobs run at once, not which jobs get to go first. The queue was still shared. The fairness problem was still there.

The fix: every store gets its own processing lane

The core insight was simple. If stores are sharing a queue, a busy store will always affect quiet ones. The only real solution is to stop sharing.

So we changed the architecture. When a Shopify store installs and loads our app, we dynamically create a dedicated Sidekiq worker for that store. Every job for that store routes to its own worker, completely isolated from every other store's workload.

Architecture diagram showing each Shopify store routing bundle jobs to its own dedicated Sidekiq worker instead of a shared queue

Now the same four-store scenario plays out differently. Store A's 1,000 jobs go into Store A's worker. Store D's single order goes into Store D's worker and processes almost immediately. They're not in the same line anymore.

Why we process one job at a time per store

Each dedicated worker processes one job at a time for its store, not in parallel.

That's deliberate. Bundle inventory updates have to be sequential. If two jobs try to update the same component's stock at the same time, you get race conditions and incorrect numbers. Processing jobs one at a time per store keeps inventory consistent without requiring complex locking logic.

The trade-off is worth it. A store that generates 1,000 orders still processes them sequentially, but that only affects that store's own queue. Everyone else moves independently.

What this actually changed

The before/after for the quiet merchant with one order is the clearest measure. Before: wait behind however many jobs were already in the shared queue, which on a busy day could mean a noticeable delay. After: their job starts almost immediately.

For high-volume stores, throughput stayed the same. They still process sequentially, they still get through their queue at the same rate. They just don't get to hold up anyone else while doing it.

A few other things improved as a side effect:

  • Failure isolation. If a worker for one store hits a problem and backs up, it doesn't cascade to other stores.

  • Cleaner monitoring. You can look at a specific store's worker and see exactly how its queue is behaving. With a shared queue, you're looking at aggregate noise.

  • No flash sale surprises. When a merchant runs a promotion, their queue grows. It used to be everyone's problem. Now it's only theirs.

Side-by-side comparison of shared Sidekiq queue versus dedicated per-store workers, showing isolation of flash sale traffic

The broader lesson from this

More servers wouldn't have fixed this. More concurrency wouldn't have fixed this. Throttling, which we already had, didn't fix this.

The problem was a design assumption baked in early: that one queue is simpler and good enough. It was simpler. It wasn't good enough once the merchant base grew.

Scaling isn't always a hardware or concurrency problem. Sometimes it's a fairness problem baked into how you've structured the work. The fix here wasn't expensive, but finding it required looking at the system from the perspective of the merchant experiencing it, not just the metrics that showed everything was technically running.

If you're seeing inventory sync delays on AI Product Bundle Builder and your store isn't particularly busy, this architecture is why it's gotten faster. The queue your store sits in is your own.

Questions about how AI Product Bundle Builder handles inventory sync? Get in touch.



Say Hello To

Essenify

Tell us about your project and we are ready

to transform your idea into

stunning digital experiences

Share this Blog
Written by
Sachin Gevariya

Sachin Gevariya is a Founder and Technical Director at Essence Solusoft. He is dedicated to making the best use of modern technologies to craft end-to-end solutions. He also has a vast knowledge of Cloud management. He loves to do coding so still doing the coding. Also, help employees for quality based solutions to clients. Always eager to learn new technology and implement for best solutions.

Say Hello To

Essenify

Tell us about your project and we are ready to transform your idea into stunning digital experiences

&

&

footer-logo

Your trusted partner for powerful Shopify apps and custom development solutions.

DMCA.com Protection Status
footer-logo

Your trusted partner for powerful Shopify apps and custom development solutions.

DMCA.com Protection Status