简体   繁体   English

活动记录中的交易

[英]transaction in activerecord

Folks, 民间,

I am fairly new to transactions in activerecord in rails and I have a piece of code, where I do something like: 我对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

My goal here for using a transaction is if I get an exception in Inventory.do_check(it is a client to external web-services and does a bunch of HTTP calls and checks) then I want the specimen.state to rollback to its previous value. 我在这里使用事务的目标是,如果我在Inventory.do_check中得到一个异常(它是外部Web服务的客户端,并且执行一堆HTTP调用和检查),那么我希望sample.state回滚到其先前的值。 I wanted to know if this will work as above? 我想知道这样是否可以正常工作? Also, it looks like on my development machine the lock is set on the entire Specimen table, when I try to query that table/model I get a BUSY exception(I am using SQLLite). 同样,在我的开发机器上,锁似乎是在整个“标本”表上设置的,当我尝试查询该表/模型时,我遇到了“忙”异常(我正在使用SQLLite)。 I was thinking that the lock should only be set on that object/record. 我以为只能在该对象/记录上设置锁定。

Any feedback is much appreciated, as I said I am really new to this so my question may be very naive. 非常感谢任何反馈,因为我说过我真的很陌生,所以我的问题可能很幼稚。

Implementation and locking depends on the DB. 实现和锁定取决于数据库。 I don't use SQLLite and I won't be surprised if it locks the entire table in such case. 我不使用SQLLite,并且在这种情况下锁定整个表也不会感到惊讶。 But reading should still work, so it's probably because it doesn't allow two concurrent operations on a single connection, so is waiting for your transaction to finish before allowing any other operation. 但是读取仍然可以工作,因此可能是因为它不允许在单个连接上执行两个并发操作,因此在允许任何其他操作之前等待事务完成。 See, for example, this SO answer: https://stackoverflow.com/a/7154699/2117020 . 例如,请参见以下SO答案: https : //stackoverflow.com/a/7154699/2117020

However, my main point is you shouldn't be holding the transaction while accessing external services in any case. 但是,我的主要观点是,无论如何在访问外部服务时都不应该暂停事务。 However it is implemented, keeping the transaction for seconds is not what you'd want. 不管它实现了多少秒,这都不是您想要的。 Looks like in your case all you want is to recover from an exception. 在您的情况下,看起来您想要的只是从异常中恢复。 Do you simply want to set the state to "FAIL" or "initial" as a result, or does do_check() modify your specimen? 您是否只是想将状态设置为“失败”或“初始”,还是do_check()修改了样品? If do_check() doesn't modify the specimen, you should better do something like: 如果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

The locking is going to be highly dependent on your database. 锁定将高度依赖于您的数据库。 You could use a row lock. 您可以使用行锁。 Something like this: 像这样:

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

Checking the external site in a transaction is not ideal, but if you use with_lock and your database supports row lock, you should just be locking this single row (it will block reads, so use carefully) 检查事务中的外部站点并不理想,但是如果使用with_lock并且数据库支持行锁,则应该仅锁定此单行(它将阻止读取,因此请谨慎使用)

Take a look at the pessimistic locking documentation in active record: http://ruby-docs.com/docs/ruby_1.9.3-rails_3.2.2/Rails%203.2.2/classes/ActiveRecord/Locking/Pessimistic.html 查看活动记录中的悲观锁定文档: http : //ruby-docs.com/docs/ruby_1.9.3-rails_3.2.2/Rails%203.2.2/classes/ActiveRecord/Locking/Pessimistic.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM