Autoload related race condition in Cucumber with AJAX requests - ruby-on-rails

I'm using Cucumber with capybara-webkit for my app's integration tests on Ruby 2.0.0, Rails 4.1. A handful of test in my cucumber test suite unexpectedly began spitting out errors like this:
Circular dependency detected while autoloading constant UiValidators::ParameterFinder (RuntimeError)
/Users/kingp/.rvm/gems/ruby-2.0.0-p451#triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:484:in `load_missing_constant'
/Users/kingp/.rvm/gems/ruby-2.0.0-p451#triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:180:in `const_missing'
/Users/kingp/Projects/rails-triquest/app/controllers/contacts_controller.rb:2:in `<class:ContactsController>'
/Users/kingp/Projects/rails-triquest/app/controllers/contacts_controller.rb:1:in `<top (required)>'
/Users/kingp/.rvm/gems/ruby-2.0.0-p451#triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:247:in `require'
...
The error says 'circular dependency', but it is actually thrown at any time the Rails autoloader tries to load a constant that is already in its set of loaded constants. Typically this is indeed due to a circular dependency, but I'm pretty sure that's not the case in my app. A diff between the branch with the crashing test and the stable branch I forked from shows that the only changes are to coffeescript files, view templates, a migration, and the new cucumber features I was writing. I haven't touched any controller or model code.
I ended up inserting some logging code into the rails autoloader to help me figure out what's going on:
# Inserted at activesupport-4.1.1/lib/active_support/dependencies.rb:467
_thread_id_for_debug = Thread.current.object_id
STDERR.puts "*** #{loaded.count} #{from_mod} #{const_name} - #{_thread_id_for_debug}"
loaded is a set of paths to autoloaded code files, from_mod the context where the request came from, const_name the constant we're trying to load. Which all ultimately got me this, immediately before the crash:
*** 104 Object SitesController - 70180261360940
*** 105 Object ContactsController - 70180240113760
*** 105 SitesController UiValidators - 70180261360940
*** 105 Object UiValidators - 70180261360940
*** 105 UiValidators ParameterFinder - 70180261360940
*** 107 UiValidators ParameterFinder - 70180240113760
It looks like two threads are attempting to autoload the same constant. My guess is that the name of the constant is added to Rails' set of 'loaded' constants by the first thread before it has finished loading. The second thread can't resolve the constant (since the load hasn't finished yet), asks the autoloader to find it, and the autoloader raises when it sees the constant in its 'loaded' set.
At this point in the test, two controllers (SitesController and ContactsController) are responding to AJAX requests, launched nearly simultaneously.
I have found a way to work around the crash, by just including a reference to the module UiValidators::ParameterFinder ahead of the AJAX. But this seems fragile, and also not very elegant. Short of turning on eager loading for the test environment, is there any other way to avoid this problem?

I had the same problem (without Cucumber, just Capybara & Poltergeist). setting config.eager_load = true didn't even work for me (don't quite understand why not..).
I ended up using Spring and haven't had a circular dependency error since.

I have the same issue with Rails 4.1.4 when using Sidekiq. I assume that a race condition inside the threaded Sidekiq workers caused all kinds of hijinks when const_missing inside active_support was called.
In addition to make sure that my current environment would perform eager loading i.e. via config.eager_load = true I also had to add all components that my workers were using from the lib directory into config.eager_load_paths (via config.eager_load_paths += %W(#{config.root}/lib) inside config/application.rb).
This was necessary because I assume that setting config.eager_load = true only makes Rails eager load the contents of the app/ directory.
App::Application.config.eager_load_paths
=> [
[0] "/home/archive/releases/20140721180504/app/assets",
[1] "/home/archive/releases/20140721180504/app/controllers",
[2] "/home/archive/releases/20140721180504/app/helpers",
[3] "/home/archive/releases/20140721180504/app/mailers",
[4] "/home/archive/releases/20140721180504/app/models",
[5] "/home/archive/releases/20140721180504/app/services",
[6] "/home/archive/releases/20140721180504/app/workers"
]
The combination of both seemed to have helped with the issue.

Related

RuntimeError: can't modify frozen Array (Rollbar, Rails 5.1 upgrade)

Unable to use rspec and rollbar after upgrading to rails 5.
Create a Rails 4 app
Upgrade gemfile to use rails 5
Try adding rollbar gem/support
Standard config/environment.rb:
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
Error when running rspec:
An error occurred while loading {path to specific spec file}
Failure/Error: require File.expand_path('../../config/environment', __FILE__)
RuntimeError:
can't modify frozen Array
# ./config/environment.rb:6:in `<top (required)>'
# ./spec/rails_helper.rb:5:in `<top (required)>'
...
No examples found.
In most cases, that error is a red herring for something else.
When encountering it, don't get overwhelmed with the recurrent can't modify frozen Array error messages, and instead check the very first error that appears when running a spec.
For example:
Failure/Error: validate :uniqueness, if: 'should_be_unique?'
ArgumentError: Passing string to be evaluated in :if and :unless
conditional options is not supported. Pass a symbol for an instance
method, or a lambda, proc or block, instead.
Just to add one tip on top of Maxximo Mussini's answer.
If anyone can't find the first error on the terminal, please try to run RSpec on one file, i.e. rspec spec/models/user_spec.rb
You should be able to find the root case.
In my case, I haven't updated my local .env variables that is required by User model
Hope it helps
Debugging this is not easy but one possible solution is simple. It could be a naming conflict with Rollbar, possibly something getting monkey-patched. If you're seeing this RuntimeError but not using Rollbar, see other answer.
Add a Module ("namespace" of your choice) around your app class definition in config/application.rb.
The module won't affect much. The only difference I could find is when printing out your app it will now appear as (that's how we found the fix vs a new working app):
<MyTestAPP::Application ...> instead of <Application ...>
Change:
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
end
To:
Module MyTestApp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
end
end

RuntimeError: can't add a new key into hash during iteration in Rack

I built a pretty small Rails 5.1.4 (Ruby 2.3.1) application. Once I've deployed it to production, I'm getting this particular error from time to time:
RuntimeError: can't add a new key into hash during iteration
Pointing here:
# rack/request.rb, line 67
def set_header(name, v)
#env[name] = v
end
I understand, this error happens when you're attempting to add new key to the hash while iterating over that hash. Since #env is a hash, it makes sense. But:
in a stacktrace I found nothing related to iterations over #env, it's a dead simple chain of app.call(env) calls.
this error occurs not always, but just once per hour or two, so this is also super weird to me
I can't reproduce it locally: I've tried to load server with multiple request hits, assuming this might be thread-safety issue, but locally it works like a charm...
Full stacktrace only consists of rack middlewares can be found here:
https://gist.github.com/Nattfodd/e513122400b4115a653ea38d69917a9a
Gemfile.lock:
https://gist.github.com/Nattfodd/a9015e9204544302bf3959cec466b715
Server is running with puma, config is very simple: just amount of threads and workers:
threads 0, 5
workers 5
My current ideas are:
one of monitoring gems has a bug (sentry-raven, new_relic)
concurrent-ruby has a bug (I read about one, but it was fixed in 1.0.2, and actual version I'm using for Puma is 1.0.5)
something super stupid, I missed, but I have no idea where to look, since the controller's action contains 3 lines of code and application config is mostly default...
this is something config-related, since backtrace does not contain the controller at all...
can you can paste the full stack-trace?
My assumption is set_header is getting called from method which iterating env.

Multithreading in Rails: Circular dependency detected while autoloading constant

I have a Rails app in which I have a Rake task that uses multithreading functions supplied by the concurrent-ruby gem.
From time to time I encounter Circular dependency detected while autoloading constant errors.
After Googling for a bit I found this to be related to using threading in combination with loading Rails constants.
I stumbled upon the following GitHub issues: https://github.com/ruby-concurrency/concurrent-ruby/issues/585 and https://github.com/rails/rails/issues/26847
As explained here you need to wrap any code that is called from a new thread in a Rails.application.reloader.wrap do or Rails.application.executor.wrap do block, which is what I did. However, this leads to deadlock.
The recommendation is then to use ActiveSupport::Dependencies.interlock.permit_concurrent_loads to wrap another blocking call on the main thread. However, I am unsure which code I should wrap with this.
Here's what I tried, however this still leads to a deadlock:
#beanstalk = Beaneater.new("#{ENV.fetch("HOST", "host")}:#{ENV.fetch("BEANSTALK_PORT", "11300")}")
tube_name = ENV.fetch("BEANSTALK_QUEUE_NAME", "queue")
pool = Concurrent::FixedThreadPool.new(Concurrent.processor_count * 2)
# Process jobs from tube, the body of this block gets executed on each message received
#beanstalk.jobs.register(tube_name) do |job|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
#logger.info "Received job: #{job.id}"
Concurrent::Future.execute(executor: pool) do
Rails.application.reloader.wrap do
# Stuff that references Rails constants etc
process_beanstalk_message(job.body)
end
end
end
end
#beanstalk.jobs.process!(reserve_timeout: 10)
Can anyone shed a light as to how I should solve this? The odd thing is I encounter this in production while other information on this topic seems to imply it should normally only occur in development.
In production I use the following settings:
config.eager_load = true
config.cache_classes = true.
Autoload paths for all environments are Rails default plus two specific folders ("models/validators" & "jobs/concerns").
eager_load_paths is not modified or set in any of my configs so must be equal to the Rails default.
I am using Rails 5 so enable_dependency_loading should equal to false in production.
You likely need to change your eager_load_paths to include the path to the classes or modules that are raising the errors. eager_load_paths is documented in the Rails Guides.
The problem you're running into is that Rails is not loading these constants when the app starts; it automatically loads them when they are called by some other piece of code. In a multithreaded Rails app, two threads may have a race condition when they try to load these constants.
Telling Rails to eagerly load these constants means they will be loaded once when the Rails app is started. It's not enough to say eager_load = true; you have to specify the paths to the class or module definitions as well. In the Rails application configuration, this is an Array under eager_load_paths. For example, to eager load ActiveJob classes:
config.eager_load_paths += ["#{config.root}/app/jobs"]
Or to load a custom module from lib/:
config.eager_load_paths += ["#{config.root}/lib/custom_module"]
Changing your eager load settings will affect the behavior of Rails. For example, in the Rails development environment, you're probably used to running rails server once, and every time you reload one of the endpoints it will reflect any changes to code you've made. That will not work with config.eager_load = true, because the classes are loaded once, at startup. Therefore, you will typically only change your eager_load settings for production.
Update
You can check your existing eager_load_paths from the rails console. For example, these are the default values for a new Rails 5 app. As you can see, it does not load app/**/*.rb; it loads the specific paths that Rails is expected to know about.
Rails.application.config.eager_load_paths
=> ["/app/assets",
"/app/channels",
"/app/controllers",
"/app/controllers/concerns",
"/app/helpers",
"/app/jobs",
"/app/mailers",
"/app/models",
"/app/models/concerns"]
In my gems (i.e., in plezi and iodine) I solve this with if statements, mostly.
You'll find code such as:
require 'uri' unless defined?(::URI)
or
begin
require 'rack/handler' unless defined?(Rack::Handler)
Rack::Handler::WEBrick = ::Iodine::Rack # Rack::Handler.get(:iodine)
rescue Exception
end
I used these snippets because of Circular dependency detected warnings and errors.
I don't know if this helps, but I thought you might want to try it.
I had this issue while trying out two gems that handles parallel processing;
pmap gem
parallel gem
For pmap I kept getting an error related to Celluloid::TaskTerminated and for parallel I was getting a Circular dependency detected while autoloading constant for when I ran it with more than 1 thread. I knew this issue was related to how my classes and modules were eager loading and race to be placed on a thread. I try enabling both of the configs to true config.cache_classes = true and config.eager_load = true in the development env and that did the trick for me.

Trace source of deprecation warnings in rails tests

When running my functional tests, I'm getting the following warning in one of the test cases but I can't pinpoint where it's coming from:
gems/actionpack-2.3.8/lib/action_controller/record_identifier.rb:76: warning: Object#id will be deprecated; use Object#object_id
Unfortunately that's the only line of the backtrace that's shown, even if I run it with rake test --trace, and there is no more information in log/test.log.
How can I get the full backtrace for this warning or otherwise figure out which line in my code is causing it?
To solve this you could enable the full debugging information. (see the help)
ActiveSupport::Deprecation.debug = true
As #Eric Anderson says it should be placed after Rails loads (i.e. after require 'rails/all' in application.rb) but before bundler runs to catch deprecation warning in gems (i.e. before Bundler.require(:default, Rails.env) if defined?(Bundler) in application.rb).
You can add a condition, like if ENV["DEBUG"] or if environment == :test to leave this in your config.
Had the same issue. A gem was causing a deprecation warning but I had no idea which gem since Rail's message only shows the last bit of the callstack in my code. Add the following:
module ActiveSupport::Deprecation
class << self
def deprecation_message_with_debugger(callstack, message = nil)
debugger
deprecation_message_without_debugger callstack, message
end
alias_method_chain :deprecation_message, :debugger
end
end
Placed this after Rails loads (i.e. after require 'rails/all' in application.rb) but before bunder runs to catch deprecation warning in gems (i.e. before Bundler.require(:default, Rails.env) if defined?(Bundler) in application.rb).
Now when a deprecation warning is encountered you are dropped in the debugger. You can either leave this in (and surround with a if Rails.env.test?) or remove it when you have found your issues.
When I get this kind of warning in my tests it is usually because I am using mocking model objects and am not providing all the methods that active record provides for real.
A good starting point would be the rails code itself. Looking at the source code for the action_pack gem which is referenced, the method that is causing the error is dom_id. That method generates an id for an object for use on a page. It seems to be called in a couple of places internally (unless you are calling it directly of course!) but the most likely cause appears to be calling form_for on an object.

Issue with daemons gem in Rails

I'm using daemons gem with Rails in addition to daemon_generator plugin. I'm getting this output in the daemons log file:
Logfile created on Sat May 09 20:10:35 -0700 2009 by /
-below you find the most recent exception thrown, this will be likely (but not certainly) the exception that made the application exit abnormally ***
#<NameError: uninitialized constant SmsMessage>
-below you find all exception objects found in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***
#<NoMemoryError: failed to allocate memory>
#<SystemStackError: stack level too deep>
#<fatal: exception reentered>
#<MissingSourceFile: no such file to load -- ./config/global_constants.conf>
#<NameError: uninitialized constant SmsMessage>
I'm finding very hard to make sense from this output. It's displaying different error messages and I can tell it's not it's not the ./config/global_constants.conf issues because I don't call it inside the daemon file. Plus I don't think it's a memory issues as my Mac has 2GB of memory and not many programs running. as for the SmsMessage I'm calling it normally using:
scheduledMessagesParent = SmsMessage.valid.find(:all, :conditions => {:status => $SCHEDULED_MESSAGE})
which works elsewhere in my website!!
I noticed that the logger is called using
ActiveRecord::Base.logger.info "....
Is it possible I have to specify my model's path in another way? I normally call the logger using logger.info without the need for ActiveRecord::Base. If so how to do that?
Any ideas how to go about debugging this problem? is there a way to display stack trace or better error messages?
I resolved this issue by loading the Rails environment with the Daemon. It wasn't included in the tutorial that I got the info from but it works now :)

Resources