【问题标题】:Testing parallel transactions using RSpec使用 RSpec 测试并行事务
【发布时间】:2011-01-13 16:39:41
【问题描述】:

有没有办法使用 RSpec 测试并行事务? 假设我有一个银行账户余额需要在减少或增加之前锁定在交易中。 但是,目前,虽然我关闭了 RSpec transactional_fixtures 选项,但我无法在两个单独的线程中启动 2 个并行事务。由于某种原因,两者都被挂起。 给定帐户是一个模型 那么这个规范就会挂起:

it "should ensure operations are performed correctly" do
  @account = Account.create(:balance => 0)
  threads = []
  (0..1).each do |index|
    threads << Thread.new do
      Account.transaction do
        account = Account.find(@account.id, :lock => true)
        account.balance += 100
        sleep 0.5
        account.save!
      end
    end
  end
  threads.each{|t|t.join}
  @account.reload.balance.should == 200
end

有没有什么办法让它不挂起,同时还能展示交易能力?

【问题讨论】:

    标签: transactions rspec2


    【解决方案1】:

    您需要在线程中使用不同的数据库连接。首先断开主线程,然后在每个线程中重新连接,最后在主线程中重新连接,如下所示:

    ActiveRecord::Base.connection.disconnect!
    (0..1).each do |index|
      threads << Thread.new do
        ActiveRecord::Base.establish_connection
        # ...
      end
    end
    ActiveRecord::Base.establish_connection
    

    以这种方式进行测试还有其他问题:您无法保证并发发生的时间点。红宝石 GIL 也无济于事。您应该使用 fork 而不是线程,或者使用 fork_break gem:https://github.com/remen/fork_break

    【讨论】:

      【解决方案2】:

      我想不出为什么要在同一资源上进行并行、并发事务的充分理由。在您的示例中,您究竟想达到什么目的?

      在您的代码中,您使用的是同一个实例,因此您会陷入死锁这一事实是意料之中的。您可能想重新考虑您在这里尝试做什么。

      【讨论】:

      • 你是说AR缓存我的Account.find()结果吗?
      • 没有。您遇到了死锁,因为这两个事务都在请求(并因此锁定)相同的资源,在本例中为 @account。如果你必须以这种方式使用线程,我建议使用 Mutex 类来确保你不会陷入死锁。 (谷歌互斥量和线程,你会发现很多例子)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多