From d0112946d4343dd753a6b6f51f73e4ac8811ac19 Mon Sep 17 00:00:00 2001 From: Rosa Gutierrez Date: Sun, 30 Aug 2026 19:07:03 +0200 Subject: [PATCH] Detect subclasses of the Solid Queue adapter RecurringTask#using_solid_queue_adapter? and ActiveJob::BatchId#solid_queue_job? compared the adapter's name to "solid_queue", so a job class using a subclass of ActiveJob::QueueAdapters::SolidQueueAdapter (to pick a shard at enqueue time, in the reporter's case) was treated as a foreign adapter: its recurring runs were enqueued through perform_later without a recurring execution row, and its jobs never joined a batch. Check the adapter's ancestry instead. The constant is resolvable wherever the gem is loaded, as the gem's Zeitwerk loader registers it as an autoload on ActiveJob::QueueAdapters. Fixes #489. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CACej2M9mLVDwpE3Bk8V41 --- app/models/solid_queue/recurring_task.rb | 2 +- lib/active_job/batch_id.rb | 2 +- test/models/solid_queue/batch_test.rb | 15 +++++++++++++++ test/models/solid_queue/recurring_task_test.rb | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/models/solid_queue/recurring_task.rb b/app/models/solid_queue/recurring_task.rb index 40a5531e3..776db7561 100644 --- a/app/models/solid_queue/recurring_task.rb +++ b/app/models/solid_queue/recurring_task.rb @@ -142,7 +142,7 @@ def ensure_existing_job_class end def using_solid_queue_adapter? - job_class.queue_adapter_name.inquiry.solid_queue? + job_class.queue_adapter.is_a?(ActiveJob::QueueAdapters::SolidQueueAdapter) end def enqueue_and_record(run_at:) diff --git a/lib/active_job/batch_id.rb b/lib/active_job/batch_id.rb index 5ab621513..05be6ec3e 100644 --- a/lib/active_job/batch_id.rb +++ b/lib/active_job/batch_id.rb @@ -51,7 +51,7 @@ def batch private def solid_queue_job? - self.class.queue_adapter_name == "solid_queue" + self.class.queue_adapter.is_a?(ActiveJob::QueueAdapters::SolidQueueAdapter) end end end diff --git a/test/models/solid_queue/batch_test.rb b/test/models/solid_queue/batch_test.rb index 8dab723d7..a2e936f29 100644 --- a/test/models/solid_queue/batch_test.rb +++ b/test/models/solid_queue/batch_test.rb @@ -147,6 +147,14 @@ class OtherAdapterCallbackJob < ApplicationJob def perform; end end + class SubclassedSolidQueueAdapter < ActiveJob::QueueAdapters::SolidQueueAdapter; end + + class SubclassedAdapterJob < ApplicationJob + self.queue_adapter = SubclassedSolidQueueAdapter.new + + def perform; end + end + class HookedCallbackJob < ApplicationJob cattr_accessor :enqueue_hook_ran, default: false @@ -181,6 +189,13 @@ def perform; end assert_equal 0, SolidQueue::Job.where(class_name: AbortingCallbackJob.name).count end + test "jobs using a subclass of the Solid Queue adapter belong to the batch" do + batch = SolidQueue::Batch.enqueue { SubclassedAdapterJob.perform_later } + + assert_equal 1, batch.jobs.count + assert_equal batch.id, SolidQueue::Job.where(class_name: SubclassedAdapterJob.name).sole.batch_id + end + test "callback jobs enqueue through solid_queue regardless of their class adapter" do batch = SolidQueue::Batch.enqueue(on_finish: OtherAdapterCallbackJob) do NiceJob.perform_later("world") diff --git a/test/models/solid_queue/recurring_task_test.rb b/test/models/solid_queue/recurring_task_test.rb index 518fc7ed8..23ea3f879 100644 --- a/test/models/solid_queue/recurring_task_test.rb +++ b/test/models/solid_queue/recurring_task_test.rb @@ -41,6 +41,16 @@ def perform end end + class SubclassedSolidQueueAdapter < ActiveJob::QueueAdapters::SolidQueueAdapter; end + + class JobUsingSubclassedSolidQueueAdapter < ApplicationJob + self.queue_adapter = SubclassedSolidQueueAdapter.new + + def perform + JobBuffer.add "job_using_subclassed_solid_queue_adapter" + end + end + class JobWithConcurrencyControlsAndDiscard < ApplicationJob limits_concurrency key: -> { true }, on_conflict: :discard @@ -87,6 +97,14 @@ def perform assert_equal "job_using_async_adapter", JobBuffer.last_value end + test "job using a subclass of the Solid Queue adapter" do + task = recurring_task_with(class_name: "JobUsingSubclassedSolidQueueAdapter") + + assert_difference -> { SolidQueue::RecurringExecution.count }, +1 do + enqueue_and_assert_performed_with_result task, "job_using_subclassed_solid_queue_adapter" + end + end + test "error when enqueuing job before recording task" do SolidQueue::Job.stubs(:create!).raises(ActiveRecord::Deadlocked)