hutch icon indicating copy to clipboard operation
hutch copied to clipboard

Auto-loading consumers from a Rails 6 app requires using classic autoloader

Open jtrees opened this issue 6 years ago • 10 comments

Summary

Currently Hutch does not work with Rails 6 (rc1) out-of-the-box since the latter defaults to using Zeitwerk for autoloading paths.

Steps to Reproduce

  1. Generate a new Rails 6 application.
  2. Add hutch as a dependency.
  3. Create a consumer under <rails_root>/app/consumers/foo_consumer.rb.
  4. Ensure RabbitMQ is running.
  5. Run Hutch from the Rails root directory like so: bundle exec hutch --autoload-rails

Expected Result

The output should include lines such as these:

2019-07-01T15:54:24Z 6 INFO -- found rails project (.), booting app in development environment
<...>
2019-07-01T15:54:24Z 6 INFO -- setting up queues
2019-07-01T15:54:24Z 6 INFO -- setting up queue: consumers:foo_consumer

Actual Result

The output looks more like this:

2019-07-01T15:47:48Z 6 INFO -- found rails project (.), booting app in development environment
<...>
2019-07-01T15:47:48Z 6 WARN -- no consumer loaded, ensure there's no configuration issue
2019-07-01T15:47:48Z 6 INFO -- setting up queues

Known Workaround

Add the following to <rails_root>/config/application.rb:

config.autoloader = :classic

jtrees avatar Jul 01 '19 16:07 jtrees

What can Hutch do about this?

michaelklishin avatar Jul 01 '19 21:07 michaelklishin

I've got this issue, too, and I see that the consumers are registered when the Hutch::Consumer mixin is included and this doesn't seem to play well with the development environment's lazy loading. As a temporary fix one can set config.eager_load to true or trigger the consumers' autoloading in an initializer, but I'm wondering if there are better ways to handle this issue.

pzac avatar Feb 12 '20 13:02 pzac

I'm not familiar with this Rails autoloader but in general, you have one lazy init code path that's unaware of another lazy init code path. Something (such as eager load) has to kick the tires. In case of Rails, it probably should be driven by Rails and not Hutch?

michaelklishin avatar Feb 12 '20 17:02 michaelklishin

I guess so, or the consumers registration could happen differently - I'm not sure what would work best, though. After some study I've found a better workaround for a Rails 6 app using the new zeitwerk loader, ie an initializer like this:

autoloader = Rails.autoloaders.main

Dir.glob(File.join("**", "*_consumer.rb")).each do |consumer|
  autoloader.preload(consumer)
end

pzac avatar Feb 13 '20 08:02 pzac

Would you be interested in submitting a documentation PR?

On Thu, Feb 13, 2020 at 11:31 AM Paolo Zaccagnini [email protected] wrote:

I guess so, or the consumers registration could happen differently - I'm not sure what would work best, though. After some study I've found a better workaround for a Rails 6 app using the new zeitwerk loader, ie an initializer like this:

autoloader = Rails.autoloaders.main Dir.glob(File.join("**", "*_consumer.rb")).each do |consumer| autoloader.preload(consumer)end

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/gocardless/hutch/issues/331?email_source=notifications&email_token=AAAAIQTTCH6JU6OMOUG6PADRCUAOXA5CNFSM4H4T3AAKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELT3LDY#issuecomment-585610639, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAAIQXY77UX6UXOXYY4RHLRCUAOXANCNFSM4H4T3AAA .

-- MK

Staff Software Engineer, Pivotal/RabbitMQ

michaelklishin avatar Feb 13 '20 20:02 michaelklishin

Will do, and would be glad to have some zeitwerk expert to chime in if there are better ways to tackle this issue

pzac avatar Feb 14 '20 08:02 pzac

Here it is, feel free to edit/improve/close: https://github.com/gocardless/hutch/pull/342

pzac avatar Feb 14 '20 15:02 pzac

That preload method is deprecated and was removed in Zeitwerk 2.5, released last October: https://github.com/fxn/zeitwerk/blob/eec3fb6b66394c9f04b15cff3821aca68470d1aa/CHANGELOG.md#250-20-october-2021

I'm not sure if the on_setup block is a sufficient replacement?

connorshea avatar Jul 11 '22 19:07 connorshea

If you're on Zeitwerk >2.5 and Ruby >=2.7 (I think that's when descendants was introduced?) and have a class that all your consumers descend from, adding this to your hutch.rb initializer seems to work:

Rails.autoloaders.main.on_setup do
  CustomConsumerClass.descendants.each { |cname| const_get(cname) }
end

Although I can't figure out how to get code reloading to work, I need to reboot the hutch process entirely for changes to get picked up (that was true before this change as well).

connorshea avatar Jul 11 '22 19:07 connorshea

I'd happily adopt a PR that focuses on Rails 6+ and retains compatibility with the previous autoloader if, say, the user opts-in by requiring a specific Hutch file.

michaelklishin avatar Jul 12 '22 09:07 michaelklishin