0

伙计们,

我对 rails 中的 activerecord 中的事务相当陌生,并且我有一段代码,我在其中执行以下操作:

transaction do
  specimen = Specimen.find_by_doc_id(25)
  specimen.state = "checking"
  specimen.save
  result = Inventory.do_check(specimen)
  if result
    specimen.state="PASS"
  else
    specimen.state="FAIL"
  end
  specimen.save
end

我在这里使用事务的目标是,如果我在 Inventory.do_check 中遇到异常(它是外部 Web 服务的客户端并执行大量 HTTP 调用和检查),那么我希望 sample.state 回滚到其先前的值. 我想知道这是否会像上面那样工作?此外,看起来在我的开发机器上,锁定是在整个 Specimen 表上设置的,当我尝试查询该表/模型时,我得到一个 BUSY 异常(我正在使用 SQLLite)。我在想应该只在那个对象/记录上设置锁。

非常感谢任何反馈,因为我说我对此很陌生,所以我的问题可能非常幼稚。

4

2 回答 2

1

实现和锁定取决于数据库。我不使用 SQLLite,如果它在这种情况下锁定整个表,我不会感到惊讶。但是读取应该仍然有效,所以这可能是因为它不允许在单个连接上进行两个并发操作,所以在允许任何其他操作之前等待您的事务完成。例如,请参阅此 SO 答案:https ://stackoverflow.com/a/7154699/2117020 。

但是,我的主要观点是,在任何情况下,您都不应该在访问外部服务时持有交易。然而,它是如何实现的,将事务保持几秒钟并不是您想要的。看起来在您的情况下,您想要的只是从异常中恢复。作为结果,您只是想将状态设置为“FAIL”或“initial”,还是 do_check() 修改了您的样本?如果 do_check() 不修改样本,您最好执行以下操作:

specimen = Specimen.find_by_doc_id(25)
specimen.state="checking"
specimen.save
# or simply specimen.update_attribute( :state, "checking" )

begin
  specimen.state = Inventory.do_check(specimen) ? "PASS" : "FAIL"
rescue
  specimen.state = "FAIL" # or "initial" or whatever
end
specimen.save
于 2013-02-28T17:17:12.360 回答
0

锁定将高度依赖于您的数据库。您可以使用行锁。像这样的东西:

specimen = Specimen.find_by_doc_id(25)

success = true

# reloads the record and does a select for update which locks the row until the block exits (its wrapped in a transation)
specimen.with_lock do
  result = Inventory.do_check(specimen)
  if(result)
    specimen.state="PASS"
  else
    specimen.state="FAIL"
  end
  specimen.save!
end

在事务中检查外部站点并不理想,但是如果您使用 with_lock 并且您的数据库支持行锁,您应该只锁定这一行(它会阻止读取,因此请谨慎使用)

查看活动记录中的悲观锁定文档: http ://ruby-docs.com/docs/ruby_1.9.3-rails_3.2.2/Rails%203.2.2/classes/ActiveRecord/Locking/Pessimistic.html

于 2013-02-28T17:55:36.357 回答