lib/good_job/engine.rb in good_job-3.27.2 vs lib/good_job/engine.rb in good_job-3.27.3

- old
+ new

@@ -47,20 +47,25 @@ end end end initializer "good_job.start_async" do + # This hooks into the hookable places during Rails boot, which is unfortunately not Rails.application.initialized? + # If an Adapter is initialized during boot, we want to want to start async executors once the framework dependencies have loaded. + # When exactly that happens is out of our control because gems or application code may touch things earlier than expected. + # For example, as of Rails 6.1, if an ActiveRecord model is touched during boot, that triggers ActiveRecord to load, + # which touches DestroyAssociationAsyncJob, which loads ActiveJob, which may initialize a GoodJob::Adapter, all of which + # happens _before_ ActiveRecord finishes loading. GoodJob will deadlock if an async executor is started in the middle of + # ActiveRecord loading. config.after_initialize do - GoodJob._async_ready = true - - # Ensure Active Record and Active Job are fully loaded - ActiveRecord::Base # rubocop:disable Lint/Void - ActiveJob::Base.queue_adapter - - GoodJob::Adapter.instances - .select(&:execute_async?) - .reject(&:async_started?) - .each(&:start_async) + ActiveSupport.on_load(:active_record) do + ActiveSupport.on_load(:active_job) do + GoodJob._framework_ready = true + GoodJob._start_async_adapters + end + GoodJob._start_async_adapters + end + GoodJob._start_async_adapters end end end end