我无法使用update_all
将数据存储到Postgres中。
为了解释该问题,我们有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
// ...
但是我不知道如何证明它。
EDIT1:
// 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
答案 0 :(得分:1)
(评论无法格式化,因此在答案中)
我没有交易。没有展开您的代码,我不明白为什么这不只是
Meter.transaction do
context.meter.save
interactor.call
end
答案 1 :(得分:0)
问题全与线程有关。我们发现,该问题在过去几年中仅发生过几次。所以这确实是一个极端的情况。
有两个操作。提到的一种是用户更新仪表参数,另一种是从实际设备(物理仪表)自动导入读数。 导入开始时,它将选择单位和乘数(动作1),修改读数并将其保存到DB中(动作2)。但是这两个动作不在同一事务中。因此,如果用户在这两个动作之间保存了仪表,我们将保存错误的数据,即更新单位或乘数的系数。
我们解决了在与meter.reload
的交易中使用readings.save
的问题。我们将重新加载之前和之后的仪表进行比较,如果更改了,我们必须重新计算读数。