Class: RailsPodKit::SolidQueue::SchedulerRunner
- Inherits:
-
Object
- Object
- RailsPodKit::SolidQueue::SchedulerRunner
- Defined in:
- lib/rails_pod_kit/solid_queue/scheduler_runner.rb
Overview
Runs a SolidQueue scheduler — and only the scheduler — as a supervised background thread, so the process running jobs can scale to zero.
Scaling the executor to zero is otherwise a chicken-and-egg problem: with
no executor there is no scheduler, so nothing enqueues the recurring or
scheduled jobs that would wake one. A k8s CronJob can't take over either,
because it can't own dynamic recurring tasks (created and updated at
runtime through SolidQueue.schedule_recurring_task). Moving just the
scheduler onto an always-on process — the web, or the exporter pod —
breaks the cycle: it keeps evaluating the crons and enqueueing, and the
queue depth wakes the executor.
Deliberately not the full supervisor (plugin :solid_queue): that one
forks, and its Puma watchdog takes the host process down when the
supervisor exits — which a transient Postgres disconnect is enough to
cause (rails/solid_queue#512). Here a DB blip at worst kills the scheduler
thread; the supervising timer notices on its next tick and starts a fresh
one, and the host process never notices.
Running it on every replica is safe: enqueues stay exactly-once via the
unique index on solid_queue_recurring_executions (task_key, run_at).
Constant Summary collapse
- DEFAULT_POLLING_INTERVAL =
How often the scheduler re-reads the dynamic tasks from the DB.
5- DEFAULT_SUPERVISION_INTERVAL =
How often we check that the scheduler thread is still alive.
5- SOURCE =
'rails_pod_kit.solid_queue_scheduler'
Instance Method Summary collapse
-
#initialize(polling_interval: DEFAULT_POLLING_INTERVAL, supervision_interval: DEFAULT_SUPERVISION_INTERVAL, recurring_schedule_file: nil) ⇒ SchedulerRunner
constructor
A new instance of SchedulerRunner.
- #running? ⇒ Boolean
-
#start ⇒ Object
Starts the supervisor, which starts the scheduler on its first (immediate) tick and returns without blocking.
-
#stop ⇒ Object
Graceful stop: drop the supervisor first so it can't resurrect the scheduler, then wind the scheduler down (unschedule its timers and deregister the process, instead of leaving a row to expire).
Constructor Details
#initialize(polling_interval: DEFAULT_POLLING_INTERVAL, supervision_interval: DEFAULT_SUPERVISION_INTERVAL, recurring_schedule_file: nil) ⇒ SchedulerRunner
Returns a new instance of SchedulerRunner.
41 42 43 44 45 46 47 48 |
# File 'lib/rails_pod_kit/solid_queue/scheduler_runner.rb', line 41 def initialize(polling_interval: DEFAULT_POLLING_INTERVAL, supervision_interval: DEFAULT_SUPERVISION_INTERVAL, recurring_schedule_file: nil) @polling_interval = polling_interval @supervision_interval = supervision_interval @recurring_schedule_file = recurring_schedule_file @stopping = false end |
Instance Method Details
#running? ⇒ Boolean
72 73 74 |
# File 'lib/rails_pod_kit/solid_queue/scheduler_runner.rb', line 72 def running? !!@scheduler&.alive? end |
#start ⇒ Object
Starts the supervisor, which starts the scheduler on its first (immediate) tick and returns without blocking.
52 53 54 55 56 57 58 59 |
# File 'lib/rails_pod_kit/solid_queue/scheduler_runner.rb', line 52 def start @supervisor = ::Concurrent::TimerTask.new( execution_interval: @supervision_interval, run_now: true ) { supervise } @supervisor.execute self end |
#stop ⇒ Object
Graceful stop: drop the supervisor first so it can't resurrect the scheduler, then wind the scheduler down (unschedule its timers and deregister the process, instead of leaving a row to expire).
64 65 66 67 68 69 70 |
# File 'lib/rails_pod_kit/solid_queue/scheduler_runner.rb', line 64 def stop @stopping = true @supervisor&.shutdown @supervisor = nil @scheduler&.stop @scheduler = nil end |