【发布时间】:2013-03-19 15:04:30
【问题描述】:
我在 Rails 中有一个 Transaction 模型,表示将通过信用卡收取费用的金融交易。
当一个事务被创建时,它的状态是:new。当我尝试结算费用时(将在 DelayedJob 中发生),我将状态更新为 :pending。如果状态不是:new,任何后续对charge 的调用都将被忽略。
朴素版(不关心并发):
# Trigger a card to be charged
def charge_transaction
return unless status == :new
self.transaction do
self.delay.settle_credit_card
self.update_attribute(:status, :pending)
end
end
# Actually do the charging (in a delayed worker)
def settle_credit_card
# ... Interact with our payment gateway
end
由于这是一个负载平衡的网络应用程序,我想确保我们考虑了并发性并且不会产生重复费用(由于并发请求)。我了解乐观锁定的好处,但在这种情况下,我不介意有这个关键区域,因为同时尝试收费(或以任何方式更新事务)应该是一个例外情况。
这里尝试使用悲观的行级锁定
并发版本(选项 1)
# Trigger a card to be charged
def charge_transaction
# Obtain row-lock
self.with_lock do
self.reload # Reload once lock is obtained - necessary?
# Check status after lock/reload
return unless status == :new
self.delay.settle_credit_card
self.update_attribute(:status, :pending)
end
end
并发版本(选项 2)
# Trigger a card to be charged
def charge_transaction
# Begin transaction without lock
self.transaction do
self.reload(lock: true) # Reload and obtain lock
# Check status after lock/reload
return unless status == :new
self.delay.settle_credit_card
self.update_attribute(:status, :pending)
end
end
这些方法中的任何一种(或两种)都有效吗?获得锁后是否需要显式重新加载(以确保事务对象是当前的)或者 Rails 会在获得锁时自动执行此操作?如果两种方法都有效,哪种方法更可取?
非常感谢!
【问题讨论】:
标签: ruby-on-rails-3 activerecord concurrency locking delayed-job