Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start async adapters once ActiveRecord and ActiveJob have loaded, potentially before Rails.application.initialized? #483

Merged
merged 1 commit into from
Jan 13, 2022

Conversation

bensheldon
Copy link
Owner

@bensheldon bensheldon commented Jan 13, 2022

A project was having trouble with Rails async not starting up. In poking at it, it appeared that GoodJob was being initialized in the bootup phase after after_initialize hooks had been called, but before Rails.application.initialized? was true. The result being that the Adapter does not initialize the async executor.

Details: AFAICT Rails loads routes after the after_initialize hook has been called. This project used Devise, and devise_for :user will load the User model, which kicks off a loading chain explained below. Because this happened after after_initialize but before Rails.application.initialized? == true, the GoodJob async adapter was being initialized but never started. It must be more complex though because I use Devise in my own applications and have not seen this behavior myself 🤷

The difference in behavior between after_initialize and initialized? is briefly discussed in rails/rails#32237

This PR tracks GoodJob's dependencies (ActiveRecord and ActiveJob) during the Rails boot process and attempts to start the async executors when all dependency conditions have been met.

The risk here is that there could be potential deadlocks because the executors may speculatively be started slightly earlier in the Rails boot process; but it concretely fixes a problem for this Rails application. And I think I've covered the bases.

I added a meaty comment to explain:

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 its 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.

Connects to #199, #293.

@bensheldon bensheldon temporarily deployed to goodjob-rails-init-hook-8jkjg6 January 13, 2022 02:26 Inactive
@bensheldon bensheldon temporarily deployed to goodjob-rails-init-hook-8jkjg6 January 13, 2022 05:14 Inactive
@bensheldon bensheldon changed the title Start async process upon Railtie.after_initialize hook, but potentially before Rails.application.initialized? Start async adapters once ActiveRecord and ActiveJob have loaded, potentially before Rails.application.initialized? Jan 13, 2022
@bensheldon bensheldon temporarily deployed to goodjob-rails-init-hook-8jkjg6 January 13, 2022 05:16 Inactive
… potentially before `Rails.application.initialized?`
@bensheldon bensheldon temporarily deployed to goodjob-rails-init-hook-8jkjg6 January 13, 2022 05:17 Inactive
@bensheldon bensheldon merged commit b052ed1 into main Jan 13, 2022
@bensheldon bensheldon deleted the rails_init_hook branch January 13, 2022 05:42
@bensheldon bensheldon added the bug Something isn't working label Jan 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant