0

我在使用update_all. 为了解释这个问题,我们有 2 个类,Meter并且Readings. 每个仪表都有许多读数。仪表具有属性unit,例如能量单位 kWh、MWh、... 和multiplier,将读数状态相乘以获​​得最终值。当用户想要更新Meter参数(unit, multiplier)时,我们Interactors先更新Readings状态,然后保存Meter自己。所有这些操作都发生在一个事务中,所以如果一个失败,那么所有的都会失败。但是我们遇到了这样的情况,当仪表被保存并且读数没有更新时,反之亦然。我检查了,当仪表没有正确保存时,它会导致context.fail!. Readings使用update_all没有任何成功检查,但我读到,update_all直接进入数据库,当它在约束上失败时,它会因异常而失败。

我没有找到任何方法,如何复制它。

// update readings
class Meters::ChangeUnit
   // includes

   def call
      coefficient = 1.0
      coefficient *= unit_change if context.meter.energy_unit_changed?
      coefficient *= multiplier_change if context.meter.multiplier_changed?

      return if coefficient == 1.0

      // this probably fails:
      context.meter.readings.update_all "state = state * #{coefficient}"
   end

   // ...

end

// save meter
class Meters::Save
    include Meters::BaseInteractor

    def call
        context.fail! meter_errors: context.meter.errors unless context.meter.save
    end
end

我的想法是使用这样的东西Meters::ChangeUnit call

 // ...
 cnt = context.meter.readings.count
 updated = context.meter.readings.update_all "state = state * #{coefficient}"
 unless cnt == updated
   context.fail! updated_meter_readings: "#{updated}/#{cnt}"
 end
 // ...

但我不知道如何证明这一点。

编辑1:

 // usage in cotroller
 context = UpdateMeter.call(meter: @meter, bonds_definition: params[:meters_ids])


 // UpdateMeter
 class UpdateMeter
    include Interactor::Organizer

    organize Meters::Update, ProcessAfterCommitQueue
 end




// Meters::Update
class Meters::Update
  include Interactor::Organizer
  include Interactor::InTransaction

  organize Meters::ValidateActive,
           // ...
           Meters::ChangeUnit,
           // ...
           Meters::Save,
           // ...
end



// Interactor::InTransaction
module Interactor::InTransaction
   extend ActiveSupport::Concern

   included do
      around do |interactor|
         ActiveRecord::Base.transaction { interactor.call }
      end
   end
end
4

2 回答 2

1

(评论无法格式化,所以这里是答案)

我没有看到交易。如果不展开你的代码,我不明白为什么这不仅仅是

Meter.transaction do
  context.meter.save
  interactor.call
end
于 2019-01-24T22:51:55.140 回答
0

问题都与线程有关。我们发现,这个问题在过去几年中只发生过几次。所以这真的是边缘情况。

有两种操作。一种是用户提到的仪表参数更新,另一种是从实际设备(物理仪表)自动导入读数。导入开始时,它会选择单位和乘数(操作 1),修改读数并将其保存到数据库中(操作 2)。但是这两个动作不在同一个事务中。因此,如果用户在这两个动作之间保存了仪表,我们将保存错误的数据,因为更新的单位或乘数。

我们解决了meter.reload在交易中使用readings.save. 我们比较重新加载之前和重新加载之后的仪表,如果它发生了变化,我们必须重新计算读数。

于 2019-01-27T15:42:25.800 回答