【问题标题】:Rails ActiveRecord models out-of-sync with database after Thread.joinRails ActiveRecord 模型在 Thread.join 后与数据库不同步
【发布时间】:2015-01-14 23:55:54
【问题描述】:

假设我有一个名为 Person 的 ActiveRecord 模型,其表名为 people,其中包含 idname 列。 name 列上有一个唯一键约束,因此没有两个 Person 记录可以共享一个名称。我正在使用find_or_create_by_name 加上一个救援块,以防两个线程尝试插入相同的值:

def get_or_make_person(name)
  begin
    person = Person.find_or_create_by_name(name)
  rescue ActiveRecord::RecordNotUnique
    retry
  end
  person
end

我正在尝试测试我的代码的线程安全性。于是我写了一个测试:

name = 'Joe'

t1 = Thread.new do
  person = get_or_make_person name
end

t1 = Thread.new do
  person = get_or_make_person name
end

# Wait until both threads are done
t1.join
t2.join

# At this point, the person should be created no matter what. Let's find him:
person = Person.find_by_name name
assert_not_nil person

但是,此测试失败。在加入两个线程后,我使用Pry 检查我的程序,发现我在主线程中的Person 模型对其他两个线程中创建的记录一无所知。此时手动查看数据库显示肯定在数据库中:

mysql> select * from people;
+----+------+
| id | name |
+----+------+
| 1  | Joe  |
+----+------+

我什至尝试使用Person.find_by_sql('select * from people'),但也没有返回记录。

一旦我退出调试会话并使用不同的名称(例如“Jane”)再次尝试测试,我就可以使用 Person.find_by_name('Joe')Person.find(1) 检索第一条记录。

这里发生了什么?有什么方法可以强制 ActiveRecord 重新加载其对 people 表的了解?

我的用例比这个稍微复杂一些(这就是为什么这个例子看起来有点做作),但这应该抓住我遇到的本质。

其他细节:

如果在独立脚本中运行完全相同的代码会成功运行并产生预期结果(当然使用打印语句而不是 assert_not_nil),但在使用 Test::Unit 作为单元测试运行时会失败。

【问题讨论】:

    标签: ruby-on-rails ruby database thread-safety rails-activerecord


    【解决方案1】:

    我刚刚意识到这已经在this question 中进行了解释,并在this answer 中找到了解决方案。本质上,每个测试用例都在一个事务中执行,但线程在该事务之外创建它们的记录。关闭此测试用例的事务对我有用:

    class PersonTest < ActiveSupport::TestCase
      self.use_transactional_fixtures = false
      # ...
    end
    

    【讨论】:

      猜你喜欢
      • 2015-12-07
      • 1970-01-01
      • 2011-09-19
      • 2015-07-16
      • 1970-01-01
      • 1970-01-01
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多