简体   繁体   English

用capybara-webkit检测到死锁

[英]deadlock detected with capybara-webkit

I'm trying to pass this spec : 我试图通过这个规范:

scenario "Edit a service", js: true do
  service = create_service_for(provider, title: "First service")
  fill_edit_service_form(service)
  expect(page).to have_css('#price', text: '10,00 $')
end

This is a standard rails spec using capybara. 这是使用水豚的标准导轨规格。 I am using capybara-webkit for all scenario with javascript. 我使用capybara-webkit用于javascript的所有场景。 When I'm trying to pass it, sometimes it works, sometimes it mark than there is a missing record in the database and sometime I have this error : 当我试图传递它时,有时候它会起作用,有时它标记的数据库中有一条丢失的记录,有时我有这个错误:

Run options: include {:locations=>{"./spec/acceptances/provider_services_spec.rb"=>[31]}}
[K  1) Provider Services Edit a service
     Failure/Error: Unable to find matching line from backtrace
     ActiveRecord::StatementInvalid:
       PG::TRDeadlockDetected: ERROR:  deadlock detected
       DETAIL:  Process 24164 waits for AccessExclusiveLock on relation 3446991 of database 3446538; blocked by process 24184.
       Process 24184 waits for AccessShareLock on relation 3446902 of database 3446538; blocked by process 24164.
       HINT:  See server log for query details.
       : ALTER TABLE "active_admin_comments" DISABLE TRIGGER ALL;ALTER TABLE "provider_service_territory_provideds" DISABLE TRIGGER ALL;ALTER TABLE "provider_services" DISABLE TRIGGER ALL;ALTER TABLE "provider_divisions" DISABLE TRIGGER ALL;ALTER TABLE "provider_profiles" DISABLE TRIGGER ALL;ALTER TABLE "provider_service_intervention_level_provideds" DISABLE TRIGGER ALL;ALTER TABLE "provider_service_medium_provideds" DISABLE TRIGGER ALL;ALTER TABLE "provider_service_service_provideds" DISABLE TRIGGER ALL;ALTER TABLE "regions" DISABLE TRIGGER ALL;ALTER TABLE "service_formulas" DISABLE TRIGGER ALL;ALTER TABLE "region_translations" DISABLE TRIGGER ALL;ALTER TABLE "artists" DISABLE TRIGGER ALL;ALTER TABLE "admin_users" DISABLE TRIGGER ALL;ALTER TABLE "schema_migrations" DISABLE TRIGGER ALL;ALTER TABLE "services" DISABLE TRIGGER ALL;ALTER TABLE "services_provided" DISABLE TRIGGER ALL;ALTER TABLE "territories_provided" DISABLE TRIGGER ALL;ALTER TABLE "users" DISABLE TRIGGER ALL;ALTER TABLE "countries" DISABLE TRIGGER ALL;ALTER TABLE "coupons" DISABLE TRIGGER ALL;ALTER TABLE "currencies" DISABLE TRIGGER ALL;ALTER TABLE "formulas" DISABLE TRIGGER ALL;ALTER TABLE "collector_profiles" DISABLE TRIGGER ALL;ALTER TABLE "country_translations" DISABLE TRIGGER ALL;ALTER TABLE "images" DISABLE TRIGGER ALL;ALTER TABLE "intervention_levels_provided" DISABLE TRIGGER ALL;ALTER TABLE "measure_units" DISABLE TRIGGER ALL;ALTER TABLE "media_provided" DISABLE TRIGGER ALL;ALTER TABLE "messages" DISABLE TRIGGER ALL;ALTER TABLE "page_part_translations" DISABLE TRIGGER ALL;ALTER TABLE "orders" DISABLE TRIGGER ALL;ALTER TABLE "painting_categories" DISABLE TRIGGER ALL;ALTER TABLE "page_services" DISABLE TRIGGER ALL;ALTER TABLE "page_translations" DISABLE TRIGGER ALL;ALTER TABLE "painting_category_translations" DISABLE TRIGGER ALL;ALTER TABLE "page_parts" DISABLE TRIGGER ALL;ALTER TABLE "pages" DISABLE TRIGGER ALL;ALTER TABLE "painting_provenances" DISABLE TRIGGER ALL;ALTER TABLE "painting_prices" DISABLE TRIGGER ALL;ALTER TABLE "painting_technic_translations" DISABLE TRIGGER ALL;ALTER TABLE "painting_technics" DISABLE TRIGGER ALL;ALTER TABLE "painting_type_translations" DISABLE TRIGGER ALL;ALTER TABLE "period_translations" DISABLE TRIGGER ALL;ALTER TABLE "painting_types" DISABLE TRIGGER ALL;ALTER TABLE "paintings" DISABLE TRIGGER ALL;ALTER TABLE "periods" DISABLE TRIGGER ALL
     # -e:1:in `<main>'

 1/1 |========================= 100 ==========================>| Time: 00:00:03 

Finished in 3.41 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/acceptances/provider_services_spec.rb:29 # Provider Services Edit a service
     Screenshot: /home/dougui/rails/lescollectionneurs/tmp/capybara/screenshot_2014-02-20-18-22-55.658.png

Randomized with seed 34053

I have a lock in a table. 我在桌子上锁了。 This not always the same table. 这并不总是相同的表。

It's better when I do this : 当我这样做时,它会更好:

Capybara.reset_sessions!
DatabaseCleaner.clean

Before and after the spec but it is not always working. 规范之前和之后但并不总是有效。 If I run all my specs in the same file, I does not work. 如果我在同一个文件中运行所有规范,我就无法工作。

It appends when I was working on a unrelated things. 它在我处理不相关的事情时附加。 It works with poltergeist. 它与恶作剧者合作。

This is my spec_helper file : 这是我的spec_helper文件:

ENV["RAILS_ENV"] ||= 'test'
require 'rubygems'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rails'
require 'capybara-webkit'
require 'database_cleaner'
require 'capybara/firebug'
require 'capybara-screenshot/rspec'

if defined?(Spring)
  Spring.watch "#{Rails.root}/spec/factories"
else
  require 'shoulda-matchers'
end

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  config.infer_base_class_for_anonymous_controllers = false
  config.use_transactional_fixtures = false
  config.order = "random"

  config.include FactoryGirl::Syntax::Methods
  config.include Warden::Test::Helpers, type: :feature
  config.include FeatureHelpers, type: :feature
  config.include Devise::TestHelpers, type: :controller
  config.include ControllerHelpers, type: :controller
  config.include EmailSpec::Helpers
  config.include EmailSpec::Matchers

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

  config.before(:each) do
    if example.metadata[:js]
      DatabaseCleaner.strategy = :truncation
    else
      DatabaseCleaner.start
    end
  end

  config.after(:each) do
    DatabaseCleaner.clean
    DatabaseCleaner.strategy = :transaction if example.metadata[:js]
  end
end

Capybara.javascript_driver = :webkit

include ActionDispatch::TestProcess
I18n.locale = :fr

Geocoder.configure(:lookup => :test)

Geocoder::Lookup::Test.set_default_stub(
  [
    {
      'latitude'     => 40.7143528,
      'longitude'    => -74.0059731,
      'address'      => 'New York, NY, USA',
      'state'        => 'New York',
      'state_code'   => 'NY',
      'country'      => 'United States',
      'country_code' => 'US'
    }
  ]
)

Here are the gem used : https://github.com/GCorbel/lescollectionneursassocies/blob/master/Gemfile . 以下是使用的gem: https//github.com/GCorbel/lescollectionneursassocies/blob/master/Gemfile

Any suggestions? 有什么建议?

For more details, please look at the Github repo : https://github.com/GCorbel/lescollectionneursassocies/ . 有关更多详细信息,请查看Github回购: https//github.com/GCorbel/lescollectionneursassocies/

This is happening because you have two threads, the test and the request, modifying the database asynchronously. 发生这种情况是因为您有两个线程,即测试和请求,异步修改数据库。 It looks like you've rightly been experimenting with different configurations in your spec_helper. 看起来你正确地在spec_helper中尝试了不同的配置。 I've spend a fair amount of time struggling with this same issue, and have come up with this: 我花了相当多的时间来解决同样的问题,并提出了这个问题:

# adapted from https://gist.github.com/moonfly/4950750

require 'database_cleaner'

RSpec.configure do |config|

  config.use_transactional_fixtures = false

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

  config.around( :each ) do |spec|
    if spec.metadata[:js]
      # JS => doesn't share connections => can't use transactions
      spec.run
      DatabaseCleaner.clean_with :deletion
    else
      # No JS/Devise => run with Rack::Test => transactions are ok
      DatabaseCleaner.start
      spec.run
      DatabaseCleaner.clean

      # see https://github.com/bmabey/database_cleaner/issues/99
      begin
        ActiveRecord::Base.connection.send :rollback_transaction_records, true
      rescue
      end
    end
  end

end

I keep it all in support/database_cleaner_configuration.rb, which would work for you, as well, or you can simply replace what you've got in your spec_helper. 我把它全部保存在support / database_cleaner_configuration.rb中,这对你也有用,或者你可以简单地替换spec_helper中的内容。 If this doesn't work, let me know - I have a few other ideas, but they're kookier, and not worth getting into if this works, which it probably will... 如果这不起作用,请告诉我 - 我还有一些其他的想法,但它们更怪异,如果有效的话,不值得进入,它可能会......

Note: 注意:

It might be worth mentioning that Rails 5.1+ solves the database problem. 值得一提的是,Rails 5.1+解决了数据库问题。 Eileen Uchitelle on the Rails team made the changes necessary to run ensure test threads and the Rails server can run in the same process by sharing the database connection. Rails团队的Eileen Uchitelle进行了必要的更改以运行确保测试线程,并且Rails服务器可以通过共享数据库连接在同一进程中运行。

You have the deadlock when there are two threads which run the tests. 当有两个运行测试的线程时,您会遇到死锁。 For example open a tab in your terminal and run tests with the rspec command. 例如,在终端中打开一个选项卡,然后使用rspec命令运行测试。 Immediately open second tab in the terminal and run the tests there too with the same command. 立即打开终端中的第二个选项卡,并使用相同的命令在那里运行测试。

So, I guess you just have run all tests in one tab and trying to run particular test with the rspec spec/acceptances/provider_services_spec.rb:31 command in other tab. 所以,我猜你只是在一个选项卡中运行所有测试,并尝试在其他选项卡中使用rspec spec/acceptances/provider_services_spec.rb:31命令运行特定测试。

The most probable reason is there must be some query going on through ajax / database cleaner is truncating the database prematurely while sql query is running. 最可能的原因是必须通过ajax / database clean进行一些查询,在sql查询运行时过早地截断数据库。 One of the method is to make webkit driver wait till the ajax request is finished. 其中一种方法是使webkit驱动程序等待,直到ajax请求完成。 Add this method in the test helpers and call it after clicking the button / submitting the ajax form. 在测试助手中添加此方法,并在单击按钮/提交ajax表单后调用它。

    def wait_for_ajax_to_finish
    looping = true
    loop do
      sleep 1
      begin
        count = page.evaluate_script('window.running_ajax_calls').to_i
        looping = false if count == 0
      rescue => e
        if e.message.include? 'HTMLunitCorejsJavascript::Undefined'
          raise "For 'wait for the AJAX call to finish' to work, please include culerity.js after including jQuery."
        else
          raise e
        end
      end
    end
  end

I was having different tests fail randomly (like 1 test out of 500, every other time I ran the suite). 我有不同的测试随机失败(比如500个中的1个测试,每次我运行套件)。

This may not be the prettiest solution, but I solved the problem by just rescuing and retrying the database cleaner after 2 seconds, like so: 这可能不是最漂亮的解决方案,但我通过在2秒后救援并重试数据库清理器来解决问题,如下所示:

config.after(:each) do |example|
  begin
    DatabaseCleaner.clean
  rescue Exception => e
    # Recover from thread locks and retry the database clean after a slight delay
    sleep 2
    DatabaseCleaner.clean
  end
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM