config.threadsafe! causes NameError (uninitialized constant) in production #6850

airblade opened this Issue Jun 25, 2012 · 23 comments

8 participants


I just enabled config.threadsafe! in my production environment which is served by Unicorn. Within a few minutes I was hit by a NameError (uninitialized constant) when a controller sent one of my models (Story) a message, and the model's responding method tried to enqueue another model (EmailJob) in Delayed Job:

# In a method of a `Story` instance

Rails was looking for Story::EmailJob which doesn't exist. I disabled config.threadsafe! and all was fine again.

This is on Rails 3.0.11 and Ruby 1.9.3p0.

[Reporting here as requested by @tenderlove in]

Ruby on Rails member

@airblade can you post a backtrace - either here or in a gist, thanks.


Here you go:

A NameError occurred in stories#delivery_note:

uninitialized constant Story::EmailJob
rake ( lib/rake/ext/module.rb:36:in `const_missing'
app/models/story.rb:176:in `block in deliver_delivery_note_email'
app/models/story.rb:175:in `each'
app/models/story.rb:175:in `deliver_delivery_note_email'
app/controllers/stories_controller.rb:97:in `block (2 levels) in delivery_note'
actionpack (3.0.11) lib/action_controller/metal/mime_responds.rb:192:in `call'
actionpack (3.0.11) lib/action_controller/metal/mime_responds.rb:192:in `respond_to'
app/controllers/stories_controller.rb:94:in `delivery_note'
actionpack (3.0.11) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (3.0.11) lib/abstract_controller/base.rb:150:in `process_action'
actionpack (3.0.11) lib/action_controller/metal/rendering.rb:11:in `process_action'
actionpack (3.0.11) lib/abstract_controller/callbacks.rb:18:in `block in process_action'
activesupport (3.0.11) lib/active_support/callbacks.rb:471:in `_run__4030064561465674450__process_action__3339304554387403741__callbacks'
activesupport (3.0.11) lib/active_support/callbacks.rb:410:in `_run_process_action_callbacks'
activesupport (3.0.11) lib/active_support/callbacks.rb:94:in `run_callbacks'
actionpack (3.0.11) lib/abstract_controller/callbacks.rb:17:in `process_action'
actionpack (3.0.11) lib/action_controller/metal/rescue.rb:17:in `process_action'
actionpack (3.0.11) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
activesupport (3.0.11) lib/active_support/notifications.rb:52:in `block in instrument'
activesupport (3.0.11) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (3.0.11) lib/active_support/notifications.rb:52:in `instrument'
actionpack (3.0.11) lib/action_controller/metal/instrumentation.rb:29:in `process_action'
actionpack (3.0.11) lib/abstract_controller/base.rb:119:in `process'
actionpack (3.0.11) lib/abstract_controller/rendering.rb:41:in `process'
actionpack (3.0.11) lib/action_controller/metal.rb:138:in `dispatch'
actionpack (3.0.11) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
actionpack (3.0.11) lib/action_controller/metal.rb:178:in `block in action'
actionpack (3.0.11) lib/action_dispatch/routing/route_set.rb:62:in `call'
actionpack (3.0.11) lib/action_dispatch/routing/route_set.rb:62:in `dispatch'
actionpack (3.0.11) lib/action_dispatch/routing/route_set.rb:27:in `call'
actionpack (3.0.11) lib/action_dispatch/routing/mapper.rb:39:in `call'
rack-mount (0.6.14) lib/rack/mount/route_set.rb:148:in `block in call'
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:93:in `block in recognize'
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:138:in `optimized_each'
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:92:in `recognize'
rack-mount (0.6.14) lib/rack/mount/route_set.rb:139:in `call'
actionpack (3.0.11) lib/action_dispatch/routing/route_set.rb:493:in `call'
exception_notification (2.5.2) lib/exception_notifier.rb:25:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/head.rb:14:in `call'
rack (1.2.4) lib/rack/methodoverride.rb:24:in `call'
remotipart (1.0.2) lib/remotipart/middleware.rb:30:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/flash.rb:182:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/session/abstract_store.rb:149:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/cookies.rb:302:in `call'
activerecord (3.0.11) lib/active_record/query_cache.rb:32:in `block in call'
activerecord (3.0.11) lib/active_record/connection_adapters/abstract/query_cache.rb:28:in `cache'
activerecord (3.0.11) lib/active_record/query_cache.rb:12:in `cache'
activerecord (3.0.11) lib/active_record/query_cache.rb:31:in `call'
activerecord (3.0.11) lib/active_record/connection_adapters/abstract/connection_pool.rb:354:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/callbacks.rb:46:in `block in call'
activesupport (3.0.11) lib/active_support/callbacks.rb:416:in `_run_call_callbacks'
actionpack (3.0.11) lib/action_dispatch/middleware/callbacks.rb:44:in `call'
rack (1.2.4) lib/rack/sendfile.rb:106:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/remote_ip.rb:48:in `call'
actionpack (3.0.11) lib/action_dispatch/middleware/show_exceptions.rb:47:in `call'
railties (3.0.11) lib/rails/rack/logger.rb:13:in `call'
rack (1.2.4) lib/rack/runtime.rb:17:in `call'
activesupport (3.0.11) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
railties (3.0.11) lib/rails/application.rb:168:in `call'
railties (3.0.11) lib/rails/application.rb:77:in `method_missing'
unicorn (4.2.0) lib/unicorn/http_server.rb:530:in `process_client'
unicorn (4.2.0) lib/unicorn/http_server.rb:604:in `worker_loop'
unicorn (4.2.0) lib/unicorn/http_server.rb:487:in `spawn_missing_workers'
unicorn (4.2.0) lib/unicorn/http_server.rb:137:in `start'
unicorn (4.2.0) bin/unicorn:121:in `<top (required)>'
/var/www/apps/sparkle/shared/bundle/ruby/1.9.1/bin/unicorn:19:in `load'
/var/www/apps/sparkle/shared/bundle/ruby/1.9.1/bin/unicorn:19:in `<main>'
Ruby on Rails member

Hi @airblade, could you provide a minimal app with two files that reproduces this issue? As a plan B, the minimal part of your app that may allow us to reproduce it. That'd be really helpful.


@fxn Here you go.

It's as simple and reproducible as I can get it. Hope it helps.

Ruby on Rails member

@airblade the lib folder isn't eager loaded in production - either require 'email_job' in the model file or move it within app/*. You can make a app/jobs directory if you want to keep them separate from the other models.

@pixeltrix pixeltrix closed this Jun 25, 2012
Ruby on Rails member

The app has lib in its auto load paths. I don't have here the Rails code base, but I'd expect everything in that list to be eager loaded.

Ruby on Rails member

Nope - you need to add it to eager_load_paths:

config.eager_load_paths += ["#{config.root}/lib"]

Add that and the example app works - though I'd still recommend creating an app/jobs directory.

Ruby on Rails member

Just to clarify - you only need to add it to eager_load_paths, not both. All eager_load_paths are added to autoload_paths automatically.

Ruby on Rails member

@tenderlove if we are defaulting to threadsafe! in Rails 4 then we probably want to change the generated application.rb to use eager_load_paths as that's what most people will be after.

@pixeltrix pixeltrix reopened this Jun 26, 2012
Ruby on Rails member

I would expect that to be the other way around: all auto load paths are eager load paths.

Autoloading is not threadsafe, therefore any code that the user wanted to auto load should be eager loaded, because he has put no requires for it. Don't you think? I am missing something?

Ruby on Rails member

Currently threadsafe! is disabled by default, so to get better boot times only paths marked as :eager are eager loaded in production - autoloading is still enabled so there's a difference between the two.

Ruby on Rails member

Yes, yes. My point is: if threadsafe is enabled (as it is in this issue), I'd consider a bug not to eager load everything in auto load paths.

Ruby on Rails member

Yes, the code definitely does not do that. I think that is wrong.

The user wants constant auto loading from lib (or any other directories he might configure). The right abstraction config parameter for that is config.autoload_paths in my view, config.eager_load_paths is indirect. As a user I'd expect to set my auto load paths in config.autoload_paths.

Then, since that means the user is not going to use requires for the stuff in there, it is the framework's responsibility to eager load anything in that list. It has to, because the user has clearly stated that he is not going to load that code by hand.

So I think the eager_load! method in railties/lib/rails/engine.rb should have some extra logic to cover this case if threadsafe is enabled (or boot strategy is eager).

What do you think?

/cc @josevalim


@fxn As a user, that's my reasoning too.

Ruby on Rails member

@josevalim @tenderlove could you take a look in this one and see if the work on the eager_load fixed this one?

Ruby on Rails member

I don't understand why Rails has both autoload_paths and eager_load_paths instead of simply load_paths and an option that toggles between eager and autoloading (a la #7225). Is there some case where in production you would not want to eager load everything that could be autoloaded in development? That seems like a good way to introduce bugs.

Ruby on Rails member

I agree with @fxn. But it seems that if we add that logic, there is no reason to have eager_load_paths. If you put stuff in autoload_paths, we should automatically load it, otherwise, you should just add the paths via load path manipulation (ruby -I or $LOAD_PATH) and you can require the files yourself.

How should we proceed on this? @josevalim do you have an opinion?

Ruby on Rails member

Fixeeeeeeeeeeeeeed on master! The issue was that autoload was disabled and models are not eager loaded in production when using rake.

@josevalim josevalim closed this Sep 7, 2012

@josevalim sweet -- what commit is it? (if you have it handy)

Ruby on Rails member

FWIW -- I'm getting this error, but not from a custom job in lib, for an actual model in app/models (which is presumably eager loaded)

so there is no obvious place for me to explicitly require something to fix it...

rails 3.2.8 DJ 3.0.3


@jjb jjb referenced this issue in collectiveidea/delayed_job Sep 24, 2012

model classes not loading when using threadsafe! #433


Here's a Delayed Job ticket regarding this issue: collectiveidea/delayed_job#433

Is there any particular reason that the threadsafe! behavior is not the same during rake tasks?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment