spring
spring copied to clipboard
Problem with zeitwerk autoloader
I have a Rails 6 app and when using zeitwerk autoloader mode, it looks like Spring is adding all the rb-files to the watch list. So every change in any file triggers full spring restart. In classic mode, there is no such issue. I just add config.autoloader = :classic to application.rb and it stops restarting spring server on every change.
Here are the logs in zeitwerk mode:
started on /var/folders/qk/rfns1ml107b19r47z45l1dch0000gn/T/spring-501/92640e3e802293c883d0acfcd709d0d4
[2019-09-14 13:10:03 +0300] [6675] [server] accepted client
[2019-09-14 13:10:03 +0300] [6675] [server] running command rspec
[2019-09-14 13:10:03 +0300] [6675] [application_manager:test] child not running; starting
[2019-09-14 13:10:03 +0300] [6662] [client] sending command
[2019-09-14 13:10:03 +0300] [6678] [application:test] initialized -> running
[2019-09-14 13:10:04 +0300] [6678] [application:test] got client
[2019-09-14 13:10:04 +0300] [6678] [application:test] preloading app
[2019-09-14 13:10:07 +0300] [6678] [watcher:test] watcher: add: <here goes the list of all rb-files in the project>
I wonder, why is it adding all that files.
@fxn any idea?
Not familiar with the way Spring builds this list. I'll have a look.
I have created a new Rails 6 application with files app/models/foo.rb and app/models/foo/bar.rb and I don't see them in the logs of Spring.
I tried development (bin/rails runner 1) and test (bin/rails test).
Could you please provide a minimal way to reproduce?
It looks like you have to set config.eager_load = true for development and test envs.
Here is the example app just in case https://github.com/tycooon/spring-test-app
I could reproduce.
In principle this seems to match the docs, no?
Spring will automatically detect file changes to any file loaded when the server boots. Changes will cause the affected environments to be restarted.
Eager loading happens while the application boots, so that code lives in the master process if I remember correctly how Spring works (a bit rusty, so perhaps I am wrong).
I'll see what explains the difference.
Hi, any updates on this? I turned off eager loading but this is not great for test env.
Why not? Normally you do not want to eager load the whole app to run one test.
Indeed, in the history of Spring there is a commit that skips eager loading unconditionally, later reverted to let the user decide in the config file.
Rails sets eager loading to false in the generated config/environments/test.rb, and my intuition is that Spring in zeitwerk mode is working as expected. The suspicious behavior for me is the one in classic mode.
Not eager loading is definitely the norm in the test environment.
However, this is a difference I’d like to understand anyway, but I need to find a moment to dig into Spring source code.
I have a slightly different problem, when spring is running and any change is made to a watched file, the subsequent RSpec run yields this until I either reload or stop Spring.
$ bin/rspec spec/models/project_spec.rb:416 --format documentation
.../activesupport-6.0.0/lib/active_support/dependencies/zeitwerk_integration.rb:14:in `rescue in block in clear': reloading is disabled because config.cache_classes is true (RuntimeError)
from .../activesupport-6.0.0/lib/active_support/dependencies/zeitwerk_integration.rb:12:in `block in clear'
from .../activesupport-6.0.0/lib/active_support/dependencies.rb:47:in `block in unload_interlock'
from .../activesupport-6.0.0/lib/active_support/dependencies/interlock.rb:20:in `block in unloading'
from .../activesupport-6.0.0/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
from .../activesupport-6.0.0/lib/active_support/dependencies/interlock.rb:19:in `unloading'
from .../activesupport-6.0.0/lib/active_support/dependencies.rb:47:in `unload_interlock'
from .../activesupport-6.0.0/lib/active_support/dependencies/zeitwerk_integration.rb:11:in `clear'
from .../railties-6.0.0/lib/rails/application/finisher.rb:206:in `block (2 levels) in <module:Finisher>'
from .../activesupport-6.0.0/lib/active_support/file_update_checker.rb:83:in `execute'
from .../railties-6.0.0/lib/rails/application/finisher.rb:232:in `block (3 levels) in <module:Finisher>'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:135:in `run_callbacks'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:120:in `class_unload!'
from .../railties-6.0.0/lib/rails/application/finisher.rb:231:in `block (2 levels) in <module:Finisher>'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:429:in `instance_exec'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:429:in `block in make_lambda'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:201:in `block (2 levels) in halting'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:607:in `block (2 levels) in default_terminator'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:606:in `catch'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:606:in `block in default_terminator'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:202:in `block in halting'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:514:in `block in invoke_before'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:514:in `each'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:514:in `invoke_before'
from .../activesupport-6.0.0/lib/active_support/callbacks.rb:134:in `run_callbacks'
from .../activesupport-6.0.0/lib/active_support/execution_wrapper.rb:111:in `run!'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:114:in `run!'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:53:in `block (2 levels) in reload!'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:52:in `tap'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:52:in `block in reload!'
from .../activesupport-6.0.0/lib/active_support/execution_wrapper.rb:88:in `wrap'
from .../activesupport-6.0.0/lib/active_support/reloader.rb:51:in `reload!'
from .../spring-2.1.0/lib/spring/application.rb:168:in `serve'
from .../spring-2.1.0/lib/spring/application.rb:145:in `block in run'
from .../spring-2.1.0/lib/spring/application.rb:139:in `loop'
from .../spring-2.1.0/lib/spring/application.rb:139:in `run'
from .../spring-2.1.0/lib/spring/application/boot.rb:19:in `<top (required)>'
from /Users/olivierlacan/.rbenv/versions/2.5.5/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /Users/olivierlacan/.rbenv/versions/2.5.5/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from -e:1:in `<main>'
I'm using https://github.com/jonleighton/spring-commands-rspec with RSpec which modifies bin/rspec to add:
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
Despite the error message from Zeitwerk, config.cache_classes is set to false in my development.rb environment config.