Module: RailsPodKit::SolidQueue

Defined in:
lib/rails_pod_kit/solid_queue.rb,
lib/rails_pod_kit/solid_queue/metrics.rb,
lib/rails_pod_kit/solid_queue/scheduler_runner.rb

Overview

SolidQueue integration — the pieces a Rails 8 app needs to run its job executor with scale-to-zero autoscaling (KEDA/HPA). All three are opt-in; requiring this file only defines them.

install_metrics!    the `solid_queue_backlog` / `solid_queue_latency_seconds`
                  gauges on /metrics (see Metrics)
start_scheduler!    the SolidQueue scheduler as a supervised background
                  thread on an always-on process (see SchedulerRunner)
run_exporter!       both of the above in a dedicated always-on pod, with
                  the /metrics server, blocking until SIGTERM

Nothing here loads solid_queue itself: it is the host's dependency, the same way Puma and Sidekiq are.

Wiring the two halves separately, when the always-on process is the web:

# config/initializers/rails_pod_kit.rb
RailsPodKit::SolidQueue.install_metrics!

# config/puma.rb — after_booted only runs in the real Puma process,
# never in a console, a rake task or the test suite.
after_booted { RailsPodKit::SolidQueue.start_scheduler! }
at_exit      { RailsPodKit::SolidQueue.stop_scheduler! }

Defined Under Namespace

Modules: Metrics Classes: SchedulerRunner

Class Method Summary collapse

Class Method Details

.await_shutdownObject

Blocks the main thread (the exporter and the scheduler both run on their own) until the kubelet signals. A self-pipe rather than a Queue or a Mutex: writing to an IO is one of the few things safe to do from a trap handler.



103
104
105
106
107
# File 'lib/rails_pod_kit/solid_queue.rb', line 103

def await_shutdown
  reader, writer = IO.pipe
  %w[INT TERM].each { |signal| Signal.trap(signal) { writer.puts(signal) } }
  reader.gets
end

.install_metrics!Object

Declares the queue gauges. Call from an initializer, in whichever process should publish them — see the README on keeping one source per series.

Options are Metrics.install!'s: queues: to pin the zero baseline, fail_scrape_on_error: to fail the scrape instead of serving the last reading when the DB is unreachable (the default only holds on an endpoint this collector shares with another group).



42
43
44
# File 'lib/rails_pod_kit/solid_queue.rb', line 42

def install_metrics!(**)
  Metrics.install!(**)
end

.run_exporter!(scheduler: true, metrics: {}, **scheduler_options) ⇒ Object

Runs the dedicated always-on SolidQueue pod and blocks until SIGTERM: the queue gauges, the scheduler thread and the /metrics server in one 1-replica Deployment, so both survive the web and the executor scaling to zero and each series has exactly one source.

Unlike GlobalExporter this one is not Rails-free — SolidQueue is ActiveRecord-backed and reads the app's own tables — so the host's entrypoint boots the environment first, e.g. bin/solid-queue-pod:

#!/usr/bin/env ruby
require_relative '../config/environment'
RailsPodKit::SolidQueue.run_exporter!

Pass scheduler: false to serve the gauges only (an app whose web process already hosts the scheduler), and metrics: to override the gauge options — this endpoint is the collector's own, so a collection failure fails the scrape here rather than serving the last reading.



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/rails_pod_kit/solid_queue.rb', line 86

def run_exporter!(scheduler: true, metrics: {}, **scheduler_options)
  install_metrics!(fail_scrape_on_error: true, **metrics)
  start_scheduler!(**scheduler_options) if scheduler

  warn '[rails_pod_kit] disabled — /metrics not served by the SolidQueue exporter' unless Exporter.start!

  require 'yabeda'
  Yabeda.configure! unless Yabeda.already_configured?

  await_shutdown
  stop_scheduler!
end

.scheduler_runnerObject



65
66
67
# File 'lib/rails_pod_kit/solid_queue.rb', line 65

def scheduler_runner
  @scheduler_runner
end

.start_scheduler!Object

Starts the supervised scheduler thread and returns the runner. Idempotent: a second call returns the running one rather than starting a second scheduler in the same process.

Not gated on RailsPodKit.enabled? — that switch owns the metrics exporter, and an app may well want the scheduler with metrics turned off. The guard against starting one in a console or in specs is where you call this from (after_booted, or the exporter entrypoint).



54
55
56
57
58
# File 'lib/rails_pod_kit/solid_queue.rb', line 54

def start_scheduler!(**)
  return scheduler_runner if scheduler_runner

  @scheduler_runner = SchedulerRunner.new(**).start
end

.stop_scheduler!Object



60
61
62
63
# File 'lib/rails_pod_kit/solid_queue.rb', line 60

def stop_scheduler!
  @scheduler_runner&.stop
  @scheduler_runner = nil
end