I got bit by the Rails production environment today. Here’s the scenario: I have a set of classes that all mixin the same module.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module Syncable ... end class DownloadCars include Syncable ... end class DownloadTrucks include Syncable ... end |
Upon include the module “registers” the class with another class
1 2 3 4 5 6 7 8 |
module Syncable extend ActiveSupport::Concern included do |base| ApiCheck.registry[base.api_url] = base end ... end |
Once registered subclasses of ApiCheck can perform actions using the registry. Those actions are run via rake tasks. In development, when classes are autoloaded (i.e. only loaded when referenced), the rake tasks work fine. In production, however, the tasks fail because, in the context of a rake task, ApiCheck.registry is empty. None of my classes were registered. This struck me as odd because in production Rails eager loads classes. That is, Rails loads all your classes on application startup, not on demand. Sure enough if I entered a console session in production and checked the registry it was populated as expected. What was going on? After some digging around I came across this comment in production.rb:
1 2 3 4 5 |
# Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. # Rake tasks automatically ignore this option for performance. config.eager_load = true |
Right there on line 4, eager loading is ignored by rake tasks in production “for performance”. Since classes are not autoloaded in production my Syncable classes would have to be required manually in order for them to register themselves with ApiCheck .
1 2 3 4 5 6 7 |
def require_syncables return unless Rails.env.production? Dir[ File.expand_path('../../app/models/syncables/*.rb', __dir__) ].each {|src| require src } end |
Calling #require_syncables on the first line of my rake tasks ensures they work properly in production. Doing similar may save you from a headache if you’re also experiencing problems with rake tasks in Rails production.
Got questions or feedback? I want it. Drop your thoughts in the comments below or hit me @ccstump. You can also follow me on Twitter to be aware of future articles.
Thanks for reading!
Hey, thank you for your solution. I just wanted to share rails PR along with the comment on this with you guys:
https://github.com/rails/rails/pull/28209
“In the long term all the rake tasks will have eager loading disabled and we will give users a mechanism to eager load the tasks they want. We are also going to kill most of the Rake tasks in favor of Rails commands that will respect the configurations.” by rafaelfranca
So there was the idea of a “rake_eager_load” configuration but was droped since they do not want to support that. Check out custom rails commands as soon as they are available (couldn find anything about this now)
Thanks for the info Sven!