-
-
Save josevalim/470808 to your computer and use it in GitHub Desktop.
We had the exact same error with a very plain-vanilla setup using Rails and the "Devise" gem for user session management. Without going to the database in app code at all, we saw lots of:
ActiveRecord::StatementInvalid (Mysql2::Error: This connection is in use by: #<Thread:0x00000004f64740 sleep>: SELECT users
.* FROM users
WHERE users
.id
= 13 ORDER BY users
.id
ASC LIMIT 1): app/controllers/home_controller.rb:12:in `index'
We implemented the fix suggested by mikecmpbll because it seemed very reasonable, essentially forcing Mysql2 to do what the infrastructure above it should already be doing. Problem disappeared. Thanks mikecmpbll!!
thank you, muito obrigado, gracias! This has helped me resolve an error that was bugging me!
We also had some flakiness caused by AJAX requests that arrived after the test had ended. This can actually also lead to heisenbugs without a shared DB connection so instead of waiting for the calls to complete, as in @divineforest's solution, we instead failed the tests and fixed them. This is all that is needed in the test helper to detect tests that need fixing:
class ActionDispatch::IntegrationTest
def teardown
# detects both Prototype and jQuery AJAX requests
active=evaluate_script('window.Ajax ? Ajax.activeRequestCount : (window.jQuery ? jQuery.active : 0)')
assert_equal 0,active,'Active AJAX request after test end'
end
end
I'd been getting PG::UnableToSend another command is already in progress
errors when running a few concurrent AJAX requests upon loading a page. One of them would pass, the others would get a 500 with the aforementioned error.
The simple solution for me was to add config.allow_concurrency = false
to config/environments/test.rb
.
@mikecmpbll's solution with @aprescott's improvement, implemented for postgres. Thanks! Solves
PG::UnableToSend: another command is already in progress
and
undefined method 'fields'
and other flaky race errors.
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
# hack a mutex in the query execution so that we don't
# get competing queries that can timeout and not get cleaned up
module MutexLockedQuerying
@@semaphore = Mutex.new
def async_exec(*)
@@semaphore.synchronize { super }
end
end
PG::Connection.prepend(MutexLockedQuerying)
Hi there,
Currently the following code, in Sidekiq worker, works in development:
url = "http://localhost:3000/downloads/#{download.unique_id}" if Rails.env.development?
However, the following code in the same Sidekiq worker does not work in test:
url = "http://localhost:4000/downloads/#{download.unique_id}" if Rails.env.test?
I get a connection refused
error.
Capybara is configured as default_port = 4000
and run_server = true
.
Do you think I could fix this with a variant of the code you've posted here?
I gave up, and i'm using truncation in all my tests and the problem es gone.
The solution using Mutex
(thanks to @mikecmpbll) and wait_for_ajax
(thanks to @divineforest) works well for me.
Here is another tip from me.
In some occation, the undefined method 'fields'
error can occur even if you call wait_for_ajax
after triggering an Ajax call.
In fact, we have to wait for all database accesses are completed, not just for ajax.
Suppose that you have following scenario among others:
scenario 'Update account' do
visit edit_account_path
fill_in 'account_name', with: 'foo'
find('#update-account').click # Triggers an Ajax call
wait_for_ajax
user.reload
expect(user.name).to eq('foo')
end
If any database access occurs after this scenario ends, we will get the undefined method 'fields'
error on the next scenario.
To prevent this problem, we have to write another expectation that is fulfilled only after all database accesses are completed.
For example, if we know that a text appears on the browser screen at the end of scenario, we can write like this:
scenario 'Update account' do
visit edit_account_path
fill_in 'account_name', with: 'foo'
find('#update-account').click # Triggers an Ajax call
expect(page).to have_text('The account is updated.')
user.reload
expect(user.name).to eq('foo')
end
@kuroda - have you found a more systematic way to fix this issue? We're experiencing the same thing, and rather than run around fixing a bunch of tests, ideally there's a lower level way to address the problem.
We should work on a reliable solution for everyone.
The best take I've seen so far is @iangreenleaf's article and gem:
http://technotes.iangreenleaf.com/posts/the-one-true-guide-to-database-transactions-with-capybara.html
https://github.com/iangreenleaf/transactional_capybara
It bundles the shared connection monkey patch and the wait_for_ajax
helper.
Version 0.1.0 has just been released with support for Capybara >= 2.6.0.
It doesn't use connection_pool nor a Mutex.
I haven't had the need for it so far, but it's just a PR away.
Sorry to dig up an old thread (no pun intended), but I've been facing this issue on a Rails 5 app recently, and @mperham's solution worked great for me!
lol yeah this thread is filled with golden lessons in concurrency.
It’s 2019, do we still need this in Rails 5.2?
@gnclmorais, it seems that this is not useful anymore: https://github.com/teamcapybara/capybara#transactions-and-database-setup if you are running Rails 5.1 +.
We had some problems with the newest versions of
connection_pool
(from version 2.1.1) and using the semaphore without the connection pool seemed to work. We also connect to more than one database, here's our snippet: