我的Rails 应用程序中有User
、Order
和模型。和之间存在多对多关系,和之间存在一对多关系。Contest
Position
User
Order
Position
Order
User
Contest
我有execute
处理订单的方法的订单处理器服务。为了防止竞争条件,我使用lock!
内部事务。
问题是,当我execute
同时在参数中调用两个或多个具有相同顺序的方法时,有时我会陷入僵局。最奇怪的是,第一个方法开始执行,而第二个方法正在等待解锁。一切如预期。但是我的第一个方法(正在执行)是锁定在中间。更奇怪的是,该方法锁定在未处理任何锁定记录的行中。另外,如果我评论一些不相关的行,并且有时(并非总是)它会锁定不更改甚至不使用任何锁定记录的行,则方法永远执行的行可能会随机更改。
这是我的execute
方法
user = order.user
contest = user.contest
ActiveRecord::Base.transaction do
# the second method awaiting here as excpected
order.lock!
# some processing stuff here like
order.executed = true
position = find_or_create_position(order)
position.lock!
# .where.not(id: order.id) just in case to ignore locked row
# but based on business logic it can't really be anyway.
#
# Plus it is not seems like the reason because
# if I comment this and the next lines deadlock occurs later
open_orders = user.orders.open.where.not(id: order.id)
# commonly method stops here
closed_orders = close_open_orders!(order, price, open_orders)
# method stops here if I comment two lines above
order.save!
# I left this line because as I remember once
# after some random changes like commenting some lines
# I got method stopped even here
commission = contest.commission * order.count
closed_orders.each { |closed_order| closed_order.save! }
# here some position processing and finally
position.save!
user.lock!
# and user processing
user.save!
end
通过在评论中处理我的意思只是将属性分配给记录。
所以这是我close_open_orders!
方法的开始
rest_count = order.count
# here it stops. each just stops without any iteration.
open_orders.each do |open_order|
但我不认为问题出在each
陈述中,因为正如我所说,如果我忽略这种方法,我就会陷入僵局。
当只执行一种方法时,一切正常。我也可以附加 pg 日志,但我没有看到任何死锁:两个选择的更新订单。而不是选择,仅user.orders.open.where.not(id: order.id)
此而已。在那之后不相关(或不相关?):
03:58:13 [21231-1] LOG: statement: set client_encoding to 'UTF8'
03:58:13 [21231-2] LOG: statement: set client_encoding to 'unicode'
03:58:13 [21231-3] LOG: statement: SET client_min_messages TO 'warning'
如果需要,我可以添加更多的 pg 日志。
UPD我正在使用 sidekiq 异步执行作业