jsh*_*kol 9 concurrency activerecord locking delayed-job ruby-on-rails-3
我在Rails中有一个交易模型,代表一个金融交易,将从信用卡中扣除.
创建事务时,它的状态为:new.当我尝试解决费用(将在DelayedJob中发生)时,我将状态更新为:pending.charge如果状态不是,则将忽略任何后续调用:new.
# 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
Run Code Online (Sandbox Code Playgroud)
由于这是一个load_balanced Web应用程序,我想确保我们考虑并发性而不是创建重复费用(由于并发请求).我理解乐观锁定的好处,但在这种情况下,我不介意拥有这个关键区域,因为同时尝试收费(或以任何方式更新交易)应该是一个例外情况.
这是尝试使用悲观的行级锁定
# 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
Run Code Online (Sandbox Code Playgroud)
# 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
Run Code Online (Sandbox Code Playgroud)
这两种方法中的任何一种(或两种)都有效吗?一旦获得锁定(确保事务对象是当前的),是否需要显式重新加载,或者Rails在获取锁定时会自动执行此操作吗?如果两种方法都有效,哪个更好?
非常感谢!
Kom*_*owy 13
这两种方法都是有效的,并且可行.但是在版本1 reload中没有必要 - 锁定会自动重新加载记录,因此您可以将其删除.
然后,如果你检查源代码的with_lock和lock!,你会发现你的两个版本是等价的100%:
def lock!(lock = true)
reload(:lock => lock) if persisted?
self
end
def with_lock(lock = true)
transaction do
lock!(lock)
yield
end
end
Run Code Online (Sandbox Code Playgroud)
使用with_lock将是最简单和首选:
# Obtain row-lock
with_lock do
# Check status after lock/reload
return unless status == :new
delay.settle_credit_card
update_attribute(:status, :pending)
end
Run Code Online (Sandbox Code Playgroud)
(注意:您可以安全地self从方法调用中删除)