35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English 35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English
Rails Solid Queue: Background Jobs in Postgres Zonder Redis of Sidekiq

Rails Solid Queue: Background Jobs in Postgres Zonder Redis of Sidekiq

Roger Heykoop
Ruby on Rails, DevOps
Rails Solid Queue in productie: Postgres-backed background jobs, configuratie, concurrency, scheduled tasks, en wanneer je beter op Sidekiq blijft.

Een klant vroeg mij vorige maand of ze hun Redis-instance konden uitzetten. Ze draaiden een redelijke Rails-app — rond de twaalf miljoen jobs per maand, niet extreem — en de Redis-node was het onderdeel van hun stack waar ze het meest over moesten nadenken. Hij had twee keer gefailover in zes maanden. Hij had eigen monitoring nodig. Hij vereiste een aparte back-upstrategie waar niemand echt vertrouwen in had. “We gebruiken hem alleen voor Sidekiq,” zeiden ze. “Kunnen we dat gewoon… niet?”

Na negentien jaar Rails heb ik de community zien pendelen tussen “databases zijn voor alles te traag” en “de database moet alles doen”. Rails Solid Queue is de meest recente zwaai richting het tweede kamp, en het is de eerste keer dat de afwegingen voor de meeste applicaties aan de goede kant uitvallen. Ik heb dit jaar drie productiesystemen van Sidekiq naar Solid Queue gemigreerd. Hier is hoe die overstap er in de praktijk uitziet, waar het loont, en waar je beter blijft waar je zit.

Wat Rails Solid Queue Eigenlijk Is

Rails Solid Queue is een database-backed queuing backend voor Active Job. Het slaat jobs op in gewone Postgres- of MySQL-tabellen en draait worker-processen die SELECT FOR UPDATE SKIP LOCKED gebruiken om werk van de queue op te pakken. Dat is de hele truc. Geen Redis, geen RabbitMQ, geen externe broker. Dezelfde Postgres-instance die al je app draait, draait nu ook je job queue.

Het wordt standaard meegeleverd met nieuwe Rails 8 applicaties. Voor bestaande apps voeg je de gem toe, installeer je een aparte database (aanbevolen, niet verplicht) en configureer je Active Job om hem te gebruiken. Sidekiq-vergelijkbare patronen zoals concurrency-limieten, prioritaire queues, scheduled jobs en recurring tasks worden allemaal ondersteund.

De feature lijst is minder belangrijk dan het operationele verhaal. Voor de meeste teams betekent Rails Solid Queue één service minder om te beheren.

Wanneer Kies je Rails Solid Queue boven Sidekiq

Ik geef klanten een simpele beslisregel: als je job throughput comfortabel op één database past, gebruik Solid Queue. Als dat niet zo is, blijf op Sidekiq.

Solid Queue is de juiste keuze wanneer:

  • Je verwerkt minder dan ongeveer 50 jobs per seconde aanhoudend. Postgres kan meer aan, maar voorbij dit punt wordt het tuning-gesprek serieus.
  • Je jobs vooral IO-bound zijn (e-mails versturen, API’s aanroepen, PDF’s genereren) in plaats van rekenintensief batch-werk.
  • Je al Postgres draait en één bewegend onderdeel minder wilt.
  • Operationele eenvoud belangrijker is dan een klein performance-voordeel.
  • Je op Rails 7.1+ met Active Job zit.

Blijf op Sidekiq wanneer:

  • Je 500+ jobs per seconde draait en elke milliseconde dispatch latency zich vertaalt naar gebruikerservaring.
  • Je afhankelijk bent van Sidekiq Pro of Enterprise features (batches, rate limiters, leader election) die Solid Queue niet één-op-één evenaart.
  • Je team diepe Sidekiq operationele reflexen heeft en de Redis-kosten onzichtbaar zijn.

De meeste Rails-apps die ik zie raken de “blijf op Sidekiq” bullets niet. Die dat wel doen zijn meestal herkenbaar — ad-tech, high-volume ingestion, realtime analytics. Voor alle anderen is Rails Solid Queue puur winst.

Rails Solid Queue Installeren

In een verse Rails 8 app zit het er al in. Voor een bestaande app:

# Gemfile
gem "solid_queue"
gem "mission_control-jobs" # optionele web UI
bundle install
bin/rails solid_queue:install
bin/rails db:migrate

De installer maakt een apart database-configuratieblok aan in config/database.yml voor de queue. Dit is de setup die ik aanbeveel — houd job-tabellen buiten je primaire database zodat een op hol geslagen backlog nooit je applicatie-queries kan blokkeren.

# config/database.yml
production:
  primary:
    <<: *default
    database: myapp_production
    username: myapp
    password: <%= ENV["DATABASE_PASSWORD"] %>
  queue:
    <<: *default
    database: myapp_production_queue
    username: myapp
    password: <%= ENV["DATABASE_PASSWORD"] %>
    migrations_paths: db/queue_migrate

Wijs Active Job erop aan:

# config/application.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }

Dat is de hele installatie. Elke bestaande ApplicationJob subklasse gaat zonder verdere wijzigingen jobs enqueuen in Postgres.

Het Worker-proces Configureren

Rails Solid Queue wordt geleverd met één supervisor-proces dat workers, dispatchers en scheduler-threads beheert. Je configureert het met een YAML-bestand.

# config/queue.yml
default: &default
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 5
      processes: 2
      polling_interval: 0.1

production:
  <<: *default
  workers:
    - queues: [ critical, high ]
      threads: 5
      processes: 1
      polling_interval: 0.1
    - queues: [ default, low ]
      threads: 10
      processes: 2
      polling_interval: 0.2
    - queues: [ mailers ]
      threads: 3
      processes: 1
      polling_interval: 0.5

De vorm is belangrijker dan de specifieke getallen. Je scheidt queues op prioriteit omdat het mengen van een rapport-job van twintig minuten met user-facing e-maillevering is hoe je één keer per week klachten krijgt dat “mails traag zijn”. Die specifieke pager-melding heb ik in mijn carrière meer dan eens gehad. Zet trage jobs in een eigen pool.

Draai de supervisor zoals elk ander long-lived proces:

bin/jobs

In productie met Kamal, systemd of foreman voeg je het toe als process type naast de web server. Geen daemon-gedoe, geen Sidekiq-specifieke init scripts.

Concurrency Controls en Unique Jobs

Dit is waar Rails Solid Queue een gat dicht dat teams vroeger naar Sidekiq Pro dwong. Je kunt concurrency-limieten per job-klasse declareren:

class BillingReportJob < ApplicationJob
  queue_as :default

  limits_concurrency to: 1, key: ->(account) { account.id }, duration: 10.minutes

  def perform(account)
    account.generate_billing_report!
  end
end

Twee dingen gebeuren met deze declaratie:

  1. Slechts één BillingReportJob per account draait tegelijkertijd in de hele fleet.
  2. De lock vervalt na tien minuten om te voorkomen dat een gecrashte worker een account permanent blokkeert.

Ik heb verstrengelde Redis-based mutex-code in drie apps vervangen door vier regels limits_concurrency. De correctheid wordt gegarandeerd door Postgres row-locks, wat een beter auditeerbaar verhaal is dan custom lock-scripts in Redis.

Scheduled en Recurring Jobs

Cron-achtige recurring jobs leven in config/recurring.yml:

production:
  cleanup_expired_sessions:
    class: SessionCleanupJob
    queue: low
    schedule: "every hour"
  send_daily_digest:
    class: DigestJob
    queue: mailers
    schedule: "at 8:00 every weekday"
    args: [ "daily" ]
  refresh_exchange_rates:
    class: ExchangeRateJob
    queue: default
    schedule: "every 15 minutes"

Geen aparte whenever gem. Geen sidekiq-cron. Geen server-crontab die uit de pas loopt met deploys. Het schema wordt meegeleverd met je code en wordt geversioneerd naast de job-klassen.

Voor eenmalige scheduled jobs werken Active Job’s set(wait: 1.hour) en set(wait_until: ...) precies zoals je zou verwachten:

WelcomeSequenceStepJob.set(wait: 3.days).perform_later(user)

De dispatcher polt de solid_queue_scheduled_executions tabel standaard elke seconde en verplaatst gereed staande jobs naar de ready queue. De latency is typisch onder twee seconden tussen geplande tijd en pickup, wat prima is voor alles wat niet tijdskritisch is tot op de milliseconde.

Rails Solid Queue Operationele Tuning

Een paar dingen die ik heb geleerd door Solid Queue in productie te draaien:

Gebruik een dedicated queue-database. Ik noemde dit hierboven al maar het is het herhalen waard. Een op hol geslagen job-enqueue loop die honderdduizend rijen schrijft zou geen invloed mogen hebben op je user-facing query plans. Postgres autovacuum handelt queue-tabellen goed af, maar isoleer ze alsnog.

Stel polling_interval per queue in. Kritieke queues krijgen 0.1 seconden. Background maintenance queues kunnen elke twee of drie seconden pollen — niemand wacht erop. Elke poll is een goedkope query, maar op schaal tellen ze op.

Monitor solid_queue_failed_executions. Failed jobs landen in een tabel, niet in een Redis set die verdwijnt bij restart. Stel een alert in voor elke rij ouder dan een uur. Ik gebruik een kleine Prometheus-exporter die eens per minuut SELECT COUNT(*) FROM solid_queue_failed_executions draait.

Let op de ready_executions queue depth. Groeiende depth betekent dat workers het niet kunnen bijbenen. De mission_control-jobs gem levert een web UI die dit real-time toont:

# config/routes.rb
authenticate :user, ->(u) { u.admin? } do
  mount MissionControl::Jobs::Engine, at: "/jobs"
end

Draai de supervisor en workers als aparte processen van de web server. In Kamal of Docker Compose betekent dit een workers role naast web. Laat je niet verleiden om bin/jobs binnen je web-container te draaien — een gedeeld process-lifecycle maakt deploys rommeliger dan nodig.

Migreren van Sidekiq naar Rails Solid Queue

Het migratiepad dat ik drie keer heb gebruikt:

  1. Installeer Solid Queue met een aparte queue-database.
  2. Laat config.active_job.queue_adapter = :sidekiq staan.
  3. Override de adapter per-job voor één low-risk job-klasse:
class LowRiskJob < ApplicationJob
  self.queue_adapter = :solid_queue
end
  1. Draai Sidekiq en Solid Queue workers een week parallel. Monitor error rates.
  2. Migreer job-klassen één voor één door de per-klasse override te verwijderen en de globale adapter om te zetten zodra de meeste jobs verplaatst zijn.
  3. Drain Sidekiq, bevestig lege queues, zet Redis uit.

De per-klasse adapter-override is het detail dat dit veilig maakt. Je hoeft nooit een big-bang switch te doen, en je kunt elke individuele job binnen een minuut rollbacken.

Voor gerelateerde productie-hardening, zie mijn posts over Postgres connection pooling met PgBouncer en Rails deployen met Kamal 2 — beide direct relevant als je Solid Queue workers naast je web-fleet draait.

Veelvoorkomende Valkuilen

Connection pool uitputting. Elke worker-thread neemt een database-connection. Tien threads maal twee processen maal twee pools (primary + queue) is veertig connecties alleen al voor Solid Queue. Dimensioneer je Postgres connection limit daarnaar of zet PgBouncer ervoor.

Jobs die hun eigen transacties openen. Als je een job-body in ActiveRecord::Base.transaction wikkelt én limits_concurrency gebruikt, kun je eindigen met geneste locks die je verrassen. Lees de gegenereerde SQL met ActiveRecord::Base.logger op DEBUG voordat je het patroon breed uitrolt.

Vergeten migraties op de queue-database te draaien. bin/rails db:migrate migreert alleen de primary. Je hebt bin/rails db:migrate:queue nodig of de gecombineerde db:migrate met SCHEMA_FORMAT=sql. Dit heeft elk team dat ik heb onboard gezet een keer laten struikelen.

Aannemen dat “Postgres oneindige capaciteit betekent”. Dat is niet zo. Voorbij ongeveer vijftig aanhoudende jobs per seconde wil je je specifieke workload profileren. Voor zwaardere doorvoer, zie de technical due diligence post voor hoe ik deze beslissingen afweeg tegen daadwerkelijke traffic-patronen.

Veelgestelde Vragen

Vervangt Rails Solid Queue Sidekiq voor alle Rails-apps?

Nee. Solid Queue is een uitstekende fit voor het merendeel van de Rails-applicaties die onder vijftig jobs per seconde draaien en al Postgres gebruiken. Voor high-throughput systemen (ad-tech, analytics, realtime ingestion) en teams die afhankelijk zijn van Sidekiq Pro of Enterprise features zoals batches en rate limiters blijft Sidekiq de sterkere keuze. De regel die ik hanteer: als je huidige Redis-instance vooral stil is, ben je een Solid Queue-kandidaat.

Kan ik Rails Solid Queue met MySQL gebruiken in plaats van Postgres?

Ja. Solid Queue ondersteunt MySQL 8+ en Postgres. Beide gebruiken SELECT FOR UPDATE SKIP LOCKED voor veilige concurrent job-pickup. Postgres is iets ergonomischer vanwege zijn betere index-only scan gedrag op de queue-tabellen, maar MySQL werkt in de praktijk prima. SQLite wordt ook ondersteund voor development maar niet aanbevolen voor productie job-queues.

Hoe handelt Rails Solid Queue failed jobs en retries af?

Failed jobs landen in de solid_queue_failed_executions tabel met hun volledige error en backtrace. Active Job’s ingebouwde retry_on en discard_on declaraties werken exact zoals met elke andere adapter. Anders dan Sidekiq’s dead set blijven failed jobs in de database staan tot je ze expliciet retried of discard. De mission_control-jobs web UI biedt een één-klik retry-knop per job.

Wat gebeurt er met scheduled jobs als een Solid Queue worker crasht?

Scheduled jobs leven in de solid_queue_scheduled_executions tabel en overleven elke worker- of supervisor-crash omdat ze in Postgres staan. Als de supervisor herstart, pakt de dispatcher op waar hij gebleven was. Dit is een van de echte verbeteringen ten opzichte van Sidekiq — scheduled werk in Sidekiq wordt in Redis vastgehouden, dus een Redis-storing zonder persistence kan scheduled jobs verliezen. Solid Queue erft alle durability-garanties van je database-backups.


Hulp nodig bij het migreren van Rails background jobs van Redis of bij het schalen van een Rails-productiestack? TTB Software is gespecialiseerd in Ruby on Rails architectuur, performance en fractional CTO werk. We doen dit al negentien jaar.

#rails-solid-queue #background-jobs #rails-8 #activejob #postgres #sidekiq-alternative #ruby
R

About the Author

Roger Heykoop is een senior Ruby on Rails ontwikkelaar met 19+ jaar Rails ervaring en 35+ jaar ervaring in softwareontwikkeling. Hij is gespecialiseerd in Rails modernisering, performance optimalisatie, en AI-ondersteunde ontwikkeling.

Get in Touch

Share this article

Need Expert Rails Development?

Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise

Schedule a Free Consultation