activerecord中的事务

时间:2013-02-28 15:10:12

标签: ruby-on-rails activerecord

民间,

我对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调用和检查),那么我希望samples.state回滚到它以前的价值。我想知道这是否会如上所述?此外,在我的开发机器上看起来锁是在整个Specimen表上设置的,当我尝试查询该表/模型时,我得到一个BUSY异常(我正在使用SQLLite)。我以为只应在该对象/记录上设置锁定。

我非常感谢任何反馈,因为我说我对此很新,所以我的问题可能很天真。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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