8
votes

I have an old Rails application upgraded to version 3.2.11 that has a lot of request specifications written using capybara version 1.0.1 and running using the selenium driver. The database are cleaned after each test using database_cleaner using the truncation strategy.

I want to use poltergeist instead of selenium and upgraded capybara from 1.0.1 to 1.1.4 to be able to use the latest version of poltergeist. Only changing the capybara gem (and its dependencies) introduced problems running my specs.

I consistently get deadlock errors from my Postgresql database in the cleanup handler after each spec. My spec_helper is pretty basic and looks like this:

RSpec.configure do |config|
  config.mock_with :rspec

  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

The error I get is like this:

An error occurred in an after hook
  ActiveRecord::StatementInvalid: PG::Error: ERROR:  deadlock detected
DETAIL:  Process 41747 waits for AccessExclusiveLock on relation 17612 of database 16396; blocked by process 41752.
Process 41752 waits for RowExclusiveLock on relation 17529 of database 16396; blocked by process 41747.
HINT:  See server log for query details.
: ALTER TABLE "aaa" ENABLE TRIGGER ALL;ALTER TABLE "bbbb" ENABLE TRIGGER ALL;ALTER TABLE "ccc" ENABLE TRIGGER ALL;
  occurred at /xxx/.bundle/gems/activerecord-3.2.11/lib/active_record/connection_adapters/postgresql_adapter.rb:652:in `async_exec'

I use FactoryGirl to create test data but otherwise nothing special IMO.

I haven't been able to figure out what holds the other end of deadlock that is created by database_cleaner. Any ideas to figure that out are most welcome.

Anybody know of any changes between capybara 1.0.1 and 1.1.4 that has changed and may have started to cause these issues?

3
I actually experienced the opposite. I had deadlocks occurring intermittently in my tests until upgrading to capybara 1.1.4 from 1.1.2. Sorry my comment isn't more helpful. =/Jon Lemmon
Well I am glad that I am not the only one experiencing deadlock issues :-) I simply fail to see why two threads at a time is accessing the tables at the same time. DatabaseClean should only work after the actual tests are complete...HakonB
Oh also, this might be a stupid question, but have you tried updating your DatabaseCleaner gem? Also, FWIW I think for us the errors only started occurring only after we started using delayed_job (even though we turned it off for the tests).Jon Lemmon
I have tried updating DatabaseCleaner as well as a lot of other used gems - so far without success. I do use DelayedJob but have also tried to remove all uses of it so it can't be source of my problem.HakonB
=( damn. well, just in case you're interested, here's the commit that fixed our issues: github.com/enspiral/loomio/commit/…. And here are the before and after test suite results: travis-ci.org/enspiral/loomio/builds/4088113 and travis-ci.org/enspiral/loomio/builds/4103871. And this is our delayed_job initializer: github.com/enspiral/loomio/blob/master/config/initializers/…Jon Lemmon

3 Answers

5
votes

I got around this issue in cucumber by placing

sleep 0.2

at the end of the step (or in your case "spec") that does some AJAX stuff. I imagine what happens is that cucumber/rspec calls database cleaner while the JS driver is still waiting for the ajax response.

5
votes

The fix isn't to use sleep but to only use Capybara API methods as they wait for what's expected.

Below, line 2 fails (as current_path is non-waiting, but line 3 works (as has_selector? waits). The link to Jonas Nicklas' article below explains it well.

click_on 'signup_button'  # Which does an AJAX redirect to /dashboard
assert_equal dashboard_path, current_path  # This causes the deadlock error as Capybara doesn't wait.
assert page.has_selector?("#dashboard")  # This works as it causes Capybara to wait for the new page.

http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara

1
votes

The solution we use is to find something in the page that should change in response to a successful ajax call. So something like:

click_on('Save')
expect(page).to have_content('Saved')