4

这是一个我几乎一无所知的领域,所以提前道歉。我有一套超过 800 个 rspec 测试。在运行整个集合或特定测试文件时突然莫名其妙地,在其中几个之后(比如 20 个左右,尽管它从来不是完全相同的数字),每个测试都开始失败并出现相同的错误:

 Failure/Error: Unable to find matching line from backtrace
 ActiveRecord::ConnectionTimeoutError:
   could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)

在典型的运行中,我将在 20 次左右的请求测试后开始收到这些错误,其余 780 多次测试都失败,并出现上述完全相同的错误。我尝试回到之前完美测试过的 git commit 和分支。没有运气——仍然有 780 多次失败。还完全删除了重新创建的测试数据库。也没有运气。

我已经阅读了很多关于连接池等的线程,但恐怕我不知道如何诊断甚至发生了什么。以下是我现在所知道的事实:

  • 使用 Postgresql
  • 据我所知,开发环境运行良好
  • 在一切正常和现在之间,我没有对我知道的环境进行任何更改/升级。甚至没有任何数据库迁移。仅更改模型、视图和控制器代码。正如我所提到的,回到以前的提交并不能解决任何问题。
  • config.use_transactional_fixtures = false在 spec_helper.rb 中,因为我正在通过 Selenium 测试 ajax 功能。
  • 但是,无论 Selenium 是否用于特定的一组测试,测试都会失败。即使我只运行不使用 Selenium 的测试,在 20 次左右的测试后仍然会出现故障。

我使用的是具有以下配置的 Database Cleaner,而不是事务性装置:

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

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end

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

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

有什么想法吗?更具体地说,我应该在哪里寻找问题所在的任何想法?我几乎没有处理这种类型的 ActiveRecord 问题的经验,我什至不知道从哪里开始。

更新虽然我仍然不知道为什么会发生这种情况,但我确实知道具体是什么代码导致了它。在最近的一次提交中,我在新线程中添加了发送通知电子邮件。这是代码:

    def teacher_notification_email
      Thread.new do
        UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
        ActiveRecord::Base.connection.close
      end
    end

我在应用程序的许多其他地方都使用了这种精确的模式(使用不同的电子邮件),所有这些都经过测试。出于某种原因,这个特定的原因导致数据库超时错误。欢迎任何关于为什么会发生这种情况的想法。

更新 由于我不处于了解线程在这种情况下如何工作的阶段,因此我不知道问题的确切来源,除此之外:根据我所阅读的内容,很难以编程方式控制执行以这种方式创建的线程。但是,我找到了解决方案。而不是上面的块,我已将块更改为以下内容:

    def teacher_notification_email
      if Rails.env.test?
        UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
      else
        Thread.new do
          UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
          ActiveRecord::Base.connection.close
        end
      end
    end

所以我基本上运行不同的代码进行测试而不是开发 - 没有新的测试线程。我假设这是一个坏主意,但是直到我能理解真正的问题在哪里(无论出于何种原因,测试本身不会失败,或者仍然使用不强制测试失败的线程的代码),这个是我需要去的。

最终更新

我已经放弃了Thread.new异步发送电子邮件的方法,而是实现了 Sidekiq。这是一个多一点的工作,但它运作良好并且测试良好......

4

1 回答 1

6

这似乎与在生成的线程中触摸活动记录有关。看起来数据库连接直到它被收割后才会返回到池中。我已经能够通过提前明确要求连接并在完成后关闭它来解决此问题。尝试这个:

Thread.new do
    ActiveRecord::Base.connection_pool.with_connection do |conn|
        UserMailer.accepted_parent_invitation_email(@parent_profile).deliver
    end
end
于 2014-07-01T01:33:57.000 回答