Solid Queue’s New Fiber Workers Are a Trap for the Lazy

You deployed Solid Queue 1.6.0, saw the fiber workers option, flipped it on, and patted yourself on the back. Lower memory overhead. More concurrency. Free speed. Right?

Wrong.

Here’s what actually happens: your I/O-bound jobs hum along beautifully on fibers — API calls, database queries, HTTP fetches. Then someone enqueues a CPU-heavy report generation job. That fiber grabs the thread and refuses to let go until it’s done. Every other fiber behind it? Stuck. Waiting. Your \”concurrency\” just became serial execution with extra steps.

Fibers don’t give you parallelism. They give you the illusion of parallelism — and the illusion is what kills you in production.

The cooperative scheduling model that makes fibers so lightweight is the same mechanism that makes them dangerous for mixed workloads. Threads get preempted by the OS scheduler. Fibers yield voluntarily. That’s the whole pitch: less context-switching overhead, less memory, more efficient I/O handling. But it only works if every job plays nice and yields control back.

Your webhook delivery job? It yields. It waits on network I/O. The fiber scheduler hops to the next ready fiber. Beautiful.

Your PDF generation job that crunches 50,000 records? It never yields. It hogs the thread. And because Solid Queue’s fiber workers run on a fixed pool of threads — typically matching your CPU count — that one greedy job just cannibalized a quarter of your throughput.

This isn’t a bug. It’s the fundamental trade-off of cooperative multitasking, and it’s been documented since the 1980s. But somehow, every time a new generation of developers discovers fibers, we collectively forget.

The Solid Queue 1.6.0 release notes mention fiber workers as a feature. What they don’t mention — at least not loudly enough — is that this feature implicitly demands you rethink your queue architecture entirely.

A single queue handling both I/O-bound and CPU-bound jobs isn’t a simplification. It’s a performance regression waiting to happen.

Think about it. If you’re running Sidekiq, you probably already learned this lesson the hard way. You set up separate queues: one for fast I/O jobs, one for heavy processing, maybe one for mailers. You tuned concurrency per queue. You learned that mixing workloads is how you get latency spikes at 3 PM when someone’s cron job blocks your payment processing.

Fibers make this discipline non-negotiable.

With threads, a CPU-bound job blocks one thread but the OS scheduler keeps others running. The damage is contained. With fibers, a CPU-bound job blocks the thread, and every fiber scheduled on that thread is frozen until the job completes. The damage cascades.

The right architecture post-1.6.0 is explicit separation: one queue configured with fiber workers for I/O-bound tasks — API calls, webhooks, cache warming, notification dispatch. Another queue configured with traditional thread workers for CPU-bound tasks — report generation, image processing, data transformation. Maybe even a third queue for mixed jobs that do a bit of both.

Yes, this means more configuration. Yes, this means you have to actually think about what each job does before you enqueue it. Yes, this means tagging jobs with metadata about their resource profile.

If you’re not willing to classify your jobs, you’re not ready for fibers. Stick with threads and sleep peacefully.

The developers in the Solid Queue community are already asking the right questions. The top comment on the release asks about running separate queues with different strategies — one for I/O-bound, one for CPU-bound. That instinct is correct. The answer from the maintainers will shape how production Rails apps handle background work for years.

But here’s the twist nobody’s talking about: the real value of fiber workers isn’t the performance gain. It’s that they force you to confront a question you should have been asking all along — what kind of work is this job actually doing?

Most Rails apps have a single background job queue that’s been growing organically for years. Mailers, report generation, webhook retries, data imports, cleanup tasks — all dumped into the same queue with the same worker configuration. It works fine at small scale. It works fine until it doesn’t.

Fibers don’t create this problem. They expose it.

When threads mask the inefficiency of your queue architecture, you accumulate technical debt you can’t see. When fibers amplify that inefficiency, you finally feel the pain — and that pain is the signal you needed to fix something that was always broken.

The best feature isn’t the one that makes your code faster. It’s the one that makes your broken code obvious.

So here’s what you do. Before you enable fiber workers, audit your jobs. Literally list every job class in your app and label each one: I/O-bound or CPU-bound. If you can’t tell, it’s probably CPU-bound — I/O-bound jobs are usually obvious because they’re calling external services. Once you’ve classified, set up separate queues. Put fiber workers on the I/O queue. Leave thread workers on the CPU queue. Monitor both.

If you’re not willing to do that audit, don’t enable fibers. You’ll get a 15% memory improvement and a 200% latency regression on your busiest queue. Your users won’t notice the memory savings. They’ll absolutely notice the timeouts.

Solid Queue 1.6.0 is a genuinely good update. The fiber implementation is solid. But the feature is a scalpel, not a sledgehammer — and scalpels in untrained hands just make messier wounds.

Classify your jobs. Separate your queues. Then enjoy the fibers. That’s the order. Skip any step and you’ll learn why cooperative multitasking fell out of favor the first time around.

FAQ

Q: Aren't fibers just faster threads? Why would I not enable them everywhere?

A: Fibers aren't faster at everything — they're more memory-efficient for I/O-bound work. For CPU-bound jobs, fibers are actively worse because cooperative scheduling means a CPU-heavy job hogs the thread and freezes every other fiber on it. Threads at least get preempted by the OS scheduler.

Q: What do I actually need to change in my Solid Queue setup?

A: Split your jobs into at least two queues: one with fiber workers for I/O-bound tasks (API calls, webhooks, DB queries), one with thread workers for CPU-bound tasks (PDF generation, data processing). Tag your jobs by resource profile so they route correctly. If you can't classify your jobs, don't enable fibers yet.

Q: Is Solid Queue making a mistake by not enforcing queue separation?

A: No — the maintainers are right to leave it flexible. The mistake would be pretending fibers are a drop-in replacement for threads. The release gives you a powerful tool; the architecture decisions are yours. That said, the docs could be louder about the CPU-bound trap.

📎 Source: View Source