Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

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

Closed
airblade opened this Issue · 23 comments

8 participants

@airblade

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
Delayed::Job.enqueue EmailJob.new(blah)

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 http://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html#comment-564682853]

@pixeltrix
Owner

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

@airblade

Here you go:

A NameError occurred in stories#delivery_note:

uninitialized constant Story::EmailJob
rake (0.9.2.2) 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>'
@fxn
Owner

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.

@airblade

@fxn Here you go.

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

@pixeltrix
Owner

@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
@fxn
Owner

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.

@pixeltrix
Owner

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.

@pixeltrix
Owner

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.

@pixeltrix
Owner

@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
@fxn
Owner

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?

@pixeltrix
Owner

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.

@fxn
Owner

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.

@fxn
Owner

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

@airblade

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

@rafaelfranca

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

@tenderlove
Owner
@jfirebaugh

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.

@tenderlove
Owner

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?

@josevalim
Owner

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
@jjb

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

@jjb

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

@pixeltrix

@jjb jjb referenced this issue in collectiveidea/delayed_job
Closed

model classes not loading when using threadsafe! #433

@jjb

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
Something went wrong with that request. Please try again.