ActiveJob/Resque 脏读。事务隔离级别

ActiveJob/Resque dirty reads. Transaction isolation level

我有几个 Resque 工作人员为同一个实体(用户)服务。成功处理后,它应该减少 call_left 属性。

它与 perform_now(因此)完美配合,但与 perform_later(并行)产生不可预测的结果。在日志中有相同数量的提交 calls_left.

我尝试使用reload方法,甚至设置了最高隔离级别。但是还是有这个问题。

如何解决?

class DataProcessJob < ActiveJob::Base
  queue_as :default    
  def perform(user_id, profile_id)
    User.transaction(isolation: :serializable) do
      user = User.find(user_id).reload
      user.data_process(profile_id)
      user.update(calls_left: user.calls_left-1)
    end
  end
end

第一个选项是使用 lockingoptimistic or pessimistic). The documentation explains their differences and you can choose the one that suits your case. Also, here is a relevant code snippet 来自文档,如果您使用乐观锁定,它可能会对您有所帮助。

def with_optimistic_retry
  begin
    yield
  rescue ActiveRecord::StaleObjectError
    begin
      # Reload lock_version in particular.
      reload
    rescue ActiveRecord::RecordNotFound
      # If the record is gone there is nothing to do.
    else
      retry
    end
  end
end

第二个选项是使用原始 SQL 字符串查询来增加 calls_left 字段。底层数据库将处理原子更新。


最后但同样重要的是,您可以使用 decrement!(:calls_left) 方法使您的代码更具可读性。