From f57cac40bc7b7965efeeeae645273721452c2f1c Mon Sep 17 00:00:00 2001 From: Pissardo Date: Wed, 22 Jul 2026 19:53:10 +0200 Subject: [PATCH] Add --only-recurring to run just the recurring scheduler Makes isolating the scheduler on its own process explicit, matching the existing --skip-recurring option and the workaround of an empty workers/dispatchers config. Fixes #484 Co-authored-by: Cursor --- README.md | 4 ++++ lib/solid_queue/cli.rb | 4 ++++ lib/solid_queue/configuration.rb | 10 +++++++- test/unit/cli_test.rb | 16 +++++++++++++ test/unit/configuration_test.rb | 39 ++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02d706e6..2a25f1dd 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,8 @@ bin/jobs -c config/calendar.yml You can also skip the scheduler process by setting the environment variable `SOLID_QUEUE_SKIP_RECURRING=true`. This is useful for environments like staging, review apps, or development where you don't want any recurring jobs to run. This is equivalent to using the `--skip-recurring` option with `bin/jobs`. +To run **only** the scheduler (no workers or dispatchers)—for example to isolate recurring tasks on a dedicated process—set `SOLID_QUEUE_ONLY_RECURRING=true` or use the `--only-recurring` option with `bin/jobs`. + This is what this configuration looks like: ```yml @@ -709,6 +711,8 @@ bin/jobs --recurring_schedule_file=config/schedule.yml You can completely disable recurring tasks by setting the environment variable `SOLID_QUEUE_SKIP_RECURRING=true` or by using the `--skip-recurring` option with `bin/jobs`. +To run only the scheduler (no workers or dispatchers), set `SOLID_QUEUE_ONLY_RECURRING=true` or use `--only-recurring` with `bin/jobs`. + The configuration itself looks like this: ```yml diff --git a/lib/solid_queue/cli.rb b/lib/solid_queue/cli.rb index 11be9d12..a52e9981 100644 --- a/lib/solid_queue/cli.rb +++ b/lib/solid_queue/cli.rb @@ -20,6 +20,10 @@ class Cli < Thor desc: "Whether to skip recurring tasks scheduling", banner: "SOLID_QUEUE_SKIP_RECURRING" + class_option :only_recurring, type: :boolean, + desc: "Whether to run only the scheduler process for recurring tasks", + banner: "SOLID_QUEUE_ONLY_RECURRING" + def self.exit_on_failure? true end diff --git a/lib/solid_queue/configuration.rb b/lib/solid_queue/configuration.rb index d04a0aaa..f88ce3ed 100644 --- a/lib/solid_queue/configuration.rb +++ b/lib/solid_queue/configuration.rb @@ -43,7 +43,10 @@ def initialize(**options) end def configured_processes - if only_work? then workers + if only_work? + workers + elsif only_recurring? + schedulers else dispatchers + workers + schedulers end @@ -126,6 +129,7 @@ def default_options recurring_schedule_file: Rails.root.join(ENV["SOLID_QUEUE_RECURRING_SCHEDULE"] || DEFAULT_RECURRING_SCHEDULE_FILE_PATH), only_work: false, only_dispatch: false, + only_recurring: ActiveModel::Type::Boolean.new.cast(ENV["SOLID_QUEUE_ONLY_RECURRING"]), skip_recurring: ActiveModel::Type::Boolean.new.cast(ENV["SOLID_QUEUE_SKIP_RECURRING"]) } end @@ -142,6 +146,10 @@ def only_dispatch? options[:only_dispatch] end + def only_recurring? + options[:only_recurring] + end + def skip_recurring_tasks? options[:skip_recurring] || only_work? end diff --git a/test/unit/cli_test.rb b/test/unit/cli_test.rb index f3d5415a..45657f97 100644 --- a/test/unit/cli_test.rb +++ b/test/unit/cli_test.rb @@ -36,6 +36,22 @@ class CliTest < ActiveSupport::TestCase end end + test "only_recurring option runs just the scheduler" do + config = configuration_from_cli(only_recurring: true) + + assert_equal 1, config.configured_processes.count + assert_equal :scheduler, config.configured_processes.first.kind + end + + test "only_recurring respects SOLID_QUEUE_ONLY_RECURRING env var" do + with_env("SOLID_QUEUE_ONLY_RECURRING" => "true") do + config = configuration_from_cli + + assert_equal 1, config.configured_processes.count + assert_equal :scheduler, config.configured_processes.first.kind + end + end + test "check exits 0 and prints OK message for a valid configuration" do out, err = capture_io do assert_nothing_raised { SolidQueue::Cli.start([ "check", "--skip-recurring" ]) } diff --git a/test/unit/configuration_test.rb b/test/unit/configuration_test.rb index 7bb7f70a..1b9a9645 100644 --- a/test/unit/configuration_test.rb +++ b/test/unit/configuration_test.rb @@ -111,6 +111,45 @@ class ConfigurationTest < ActiveSupport::TestCase assert_processes configuration, :dispatcher, 1, polling_interval: 0.1, recurring_tasks: nil end + test "only_recurring runs just the scheduler process" do + configuration = SolidQueue::Configuration.new(only_recurring: true) + + assert_equal 1, configuration.configured_processes.count + assert_processes configuration, :scheduler, 1 + assert_processes configuration, :worker, 0 + assert_processes configuration, :dispatcher, 0 + assert configuration.valid? + end + + test "only_recurring ignores workers and dispatchers from the config file" do + configuration = SolidQueue::Configuration.new(only_recurring: true) + + assert_processes configuration, :worker, 0 + assert_processes configuration, :dispatcher, 0 + assert_processes configuration, :scheduler, 1 + + scheduler = configuration.configured_processes.first.instantiate + assert_has_recurring_task scheduler, key: "periodic_store_result", class_name: "StoreResultJob", schedule: "every second" + end + + test "only_recurring when SOLID_QUEUE_ONLY_RECURRING environment variable is set" do + with_env("SOLID_QUEUE_ONLY_RECURRING" => "true") do + configuration = SolidQueue::Configuration.new + + assert_equal 1, configuration.configured_processes.count + assert_processes configuration, :scheduler, 1 + assert_processes configuration, :worker, 0 + assert_processes configuration, :dispatcher, 0 + end + end + + test "only_recurring with skip_recurring is invalid" do + configuration = SolidQueue::Configuration.new(only_recurring: true, skip_recurring: true) + + assert_not configuration.valid? + assert_equal [ "No processes configured" ], configuration.errors.full_messages + end + test "skip recurring tasks when SOLID_QUEUE_SKIP_RECURRING environment variable is set" do with_env("SOLID_QUEUE_SKIP_RECURRING" => "true") do configuration = SolidQueue::Configuration.new(dispatchers: [ { polling_interval: 0.1 } ])