zeitwerk: Uninitialized constant for nested class in sidekiq after code reload with Rails 7 and bootsnap

Hi, I run into issues with sidekiq after making changes with code reload. The project is using Rails 7, Ruby 3.0.3 and Zeitwerk 2.5.1

The relevant directory structure is as follows:

app
├── test
│   ├── a.rb
│   └── b
│       └── c.rb
└── workers
    └── test_worker.rb

a.rb

class A
end

c.rb

module B
  class C
  end
end

Once the application is started everything works fine, both classes are available to rails and sidekiq with no issues. If the code is autoreloaded by making code changes B::C isn’t available to sidekiq anymore, while both classes are still accesible by rails. Adding an empty module app/test/b.rb solves the issue, but I shouldn’t have to do that.

I’m not sure if this is a problem with zeitwerk, but it looked like a good place to start since it seems to be some kind of loading issue.

Here is a minimal repro, if needed. https://github.com/Earlopain/zeitwerk-repro. It’s docker, after starting the application will be available under localhost:9000 There’s one page, which prints out A and B::C. Loading also calls a sidekiq worker, which only logs A and B::C. Again, on first load everything works as expected but after code changes sidekiq cannot find B::C anymore.

Here's the log for both rails and sidekiq
Initial boot:
22:31:17 puma.1    | Zeitwerk@rails.main: autoload set for ApplicationController, to be loaded from /demo/app/controllers/application_controller.rb
22:31:17 puma.1    | Zeitwerk@rails.main: autoload set for A, to be loaded from /demo/app/test/a.rb
22:31:17 puma.1    | Zeitwerk@rails.main: autoload set for B, to be autovivified from /demo/app/test/b
22:31:17 puma.1    | Zeitwerk@rails.main: autoload set for TestWorker, to be loaded from /demo/app/workers/test_worker.rb
22:31:17 sidekiq.1 | Zeitwerk@rails.main: autoload set for ApplicationController, to be loaded from /demo/app/controllers/application_controller.rb
22:31:17 sidekiq.1 | Zeitwerk@rails.main: autoload set for A, to be loaded from /demo/app/test/a.rb
22:31:17 sidekiq.1 | Zeitwerk@rails.main: autoload set for B, to be autovivified from /demo/app/test/b
22:31:17 sidekiq.1 | Zeitwerk@rails.main: autoload set for TestWorker, to be loaded from /demo/app/workers/test_worker.rb
First request:
22:31:19 puma.1    | Zeitwerk@rails.main: constant ApplicationController loaded from file /demo/app/controllers/application_controller.rb
22:31:19 puma.1    | Zeitwerk@rails.main: constant A loaded from file /demo/app/test/a.rb
22:31:19 puma.1    | Zeitwerk@rails.main: module B autovivified from directory /demo/app/test/b
22:31:19 puma.1    | Zeitwerk@rails.main: autoload set for B::C, to be loaded from /demo/app/test/b/c.rb
22:31:19 puma.1    | Zeitwerk@rails.main: constant B::C loaded from file /demo/app/test/b/c.rb
22:31:19 puma.1    | Zeitwerk@rails.main: constant TestWorker loaded from file /demo/app/workers/test_worker.rb
22:31:19 sidekiq.1 | Zeitwerk@rails.main: constant TestWorker loaded from file /demo/app/workers/test_worker.rb
22:31:19 sidekiq.1 | Zeitwerk@rails.main: constant A loaded from file /demo/app/test/a.rb
22:31:19 sidekiq.1 | Zeitwerk@rails.main: module B autovivified from directory /demo/app/test/b
22:31:19 sidekiq.1 | Zeitwerk@rails.main: autoload set for B::C, to be loaded from /demo/app/test/b/c.rb
22:31:19 sidekiq.1 | Zeitwerk@rails.main: constant B::C loaded from file /demo/app/test/b/c.rb
Code changed, second request:
22:34:24 puma.1    | Zeitwerk@rails.main: ApplicationController unloaded
22:34:24 puma.1    | Zeitwerk@rails.main: A unloaded
22:34:24 puma.1    | Zeitwerk@rails.main: B unloaded
22:34:24 puma.1    | Zeitwerk@rails.main: B::C unloaded
22:34:24 puma.1    | Zeitwerk@rails.main: TestWorker unloaded
22:34:24 puma.1    | Zeitwerk@rails.main: autoload set for ApplicationController, to be loaded from /demo/app/controllers/application_controller.rb
22:34:24 puma.1    | Zeitwerk@rails.main: autoload set for A, to be loaded from /demo/app/test/a.rb
22:34:24 puma.1    | Zeitwerk@rails.main: autoload set for B, to be autovivified from /demo/app/test/b
22:34:24 puma.1    | Zeitwerk@rails.main: autoload set for TestWorker, to be loaded from /demo/app/workers/test_worker.rb
22:34:24 puma.1    | Zeitwerk@rails.main: constant ApplicationController loaded from file /demo/app/controllers/application_controller.rb
22:34:24 puma.1    | Zeitwerk@rails.main: constant A loaded from file /demo/app/test/a.rb
22:34:24 puma.1    | Zeitwerk@rails.main: module B autovivified from directory /demo/app/test/b
22:34:24 puma.1    | Zeitwerk@rails.main: autoload set for B::C, to be loaded from /demo/app/test/b/c.rb
22:34:24 puma.1    | Zeitwerk@rails.main: constant B::C loaded from file /demo/app/test/b/c.rb
22:34:24 puma.1    | Zeitwerk@rails.main: constant TestWorker loaded from file /demo/app/workers/test_worker.rb
22:34:24 sidekiq.1 | Zeitwerk@rails.main: autoload for ApplicationController removed
22:34:24 sidekiq.1 | Zeitwerk@rails.main: TestWorker unloaded
22:34:24 sidekiq.1 | Zeitwerk@rails.main: A unloaded
22:34:24 sidekiq.1 | Zeitwerk@rails.main: B unloaded
22:34:24 sidekiq.1 | Zeitwerk@rails.main: B::C unloaded
22:34:24 sidekiq.1 | Zeitwerk@rails.main: autoload set for ApplicationController, to be loaded from /demo/app/controllers/application_controller.rb
22:34:24 sidekiq.1 | Zeitwerk@rails.main: autoload set for A, to be loaded from /demo/app/test/a.rb
22:34:24 sidekiq.1 | Zeitwerk@rails.main: autoload set for B, to be autovivified from /demo/app/test/b
22:34:24 sidekiq.1 | Zeitwerk@rails.main: autoload set for TestWorker, to be loaded from /demo/app/workers/test_worker.rb
22:34:24 sidekiq.1 | Zeitwerk@rails.main: constant TestWorker loaded from file /demo/app/workers/test_worker.rb
22:34:24 sidekiq.1 | Zeitwerk@rails.main: constant A loaded from file /demo/app/test/a.rb

After making changes sidekiq unloads B::C but does not reload it.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 24 (20 by maintainers)

Commits related to this issue

Most upvoted comments

First, let me congratulate you on this extremely good bug report. Terrific.

I have been able to reproduce and the behavior is puzzling, I’ll dig into it.

I will give the revised fix a try tomorrow.