The framework layer above the backend

Free preview 3 min to read

The mid-level course "Solid Queue: job architecture", earlier in this series, looked at the tables where a job's state is stored and claimed. Every one of those tables belongs to Solid Queue, one backend. This app's jobs are never written against Solid Queue directly. They're written against ActiveJob::Base, a framework layer that sits above whichever backend runs underneath.

A job from this app, in full

app/jobs/ holds several job classes. The partner integration alone has four: fetching a partner's site icon, announcing a certificate, delivering a webhook, and pruning expired handoff nonces. Here's the smallest of them, whole:

module Partners
  class FetchSiteIconJob < ApplicationJob
    queue_as :default

    def perform(partner_id)
      partner = ::Partners::Partner.find_by(id: partner_id)
      ::Partners::FetchSiteIconService.call(partner: partner) if partner && !partner.logo.attached?
    end
  end
end

ApplicationJob inherits from ActiveJob::Base, and neither class mentions Solid Queue, its tables or FOR UPDATE SKIP LOCKED. queue_as and perform are ActiveJob vocabulary, and they work the same whether the backend is Solid Queue, Sidekiq or an in-process thread pool.

The same call, different behaviour per environment

The app sets queue_adapter per environment in config/environments/*.rb:

# development.rb
config.active_job.queue_adapter = :async

# test.rb / production.rb
config.active_job.queue_adapter = :solid_queue

::Partners::FetchSiteIconJob.perform_later(partner.id), called from Admin::Partners::SavePartnerService when a partner has no logo yet, is one line of code. In development, :async runs it on an in-process thread pool: no table, no persistence, and a server restart loses anything still queued. In test and production, :solid_queue writes a durable row to solid_queue_jobs, and a worker claims it later. Same call, same job class, two different mechanisms, decided by the environment and not by anything in FetchSiteIconJob.

What this course is about

Each lesson ahead looks at one thing ActiveJob::Base provides: argument serialisation, retry_on and discard_on, limits_concurrency, queue_as, and callbacks. It checks each one against this app's jobs, its configuration, and the Solid Queue tables underneath. It doesn't repeat the internals from "Solid Queue: job architecture"; this is the layer above them.

Key takeaways

  • FetchSiteIconJob is written entirely in ActiveJob vocabulary; nothing in it names Solid Queue, its tables or how it claims work.
  • The app sets queue_adapter per environment: :async in development, :solid_queue in test and production.
  • The same perform_later call runs on an in-process thread pool with no persistence in development, and on Solid Queue's durable tables in test and production.
  • This course covers the framework layer ActiveJob::Base provides above any one backend, building on "Solid Queue: job architecture" without repeating it.
Lesson 1 of 7 · free

Keep going with lesson 2

Arguments across the wire: GlobalID vs. a plain ID

Compares GlobalID arguments, which raise on deleted records, with plain ids and find_by, which return nil.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...