我有一个非常简单的场景,我正在创建一条记录,然后附加一个文件(之后save!
,因为我需要记录上的 id 来生成附件的细名),所有这些都包含在事务中。
就像是:
def create
ActiveRecord::Base.transaction do
record = A.create!(a_params)
pdf = generate_pdf
record.file.attach(
io: StringIO.new(pdf),
filename: "PO##{record.id}.pdf",
content_type: 'application/pdf'
)
rescue
# here it should rollback transaction on all kind of errors, if it fails upload or whatever, but it does not
raise ActiveRecord::Rollback
end
end
但是 activestorage 仅在提交发生后才上传文件,因此这种内部救援永远不会起作用,有效的是:
def create
record = nil
ActiveRecord::Base.transaction do
record = A.create!(a_params)
pdf = generate_pdf
record.file.attach(
io: StringIO.new(pdf),
filename: "PO##{record.id}.pdf",
content_type: 'application/pdf'
)
end
rescue
record&.destroy!
end
在这里我简化了这个场景,实际上我有一个场景,在循环中创建大量记录,并且我不想保存任何记录以防出现任何错误。
我发现了一些问题,例如:https://github.com/rails/rails/issues/32449 https://github.com/rails/rails/issues/32449
https://github.com/rails/rails/issues/31985 https://github.com/rails/rails/issues/31985
我怎样才能以最好的方式解决这个问题,我读过主动存储在后台上传文件,不会使交易持续很长时间。因为否则会产生问题。这是有道理的,我认为我也应该将 pdf_ Generation 逻辑从事务中取出。
But 我想知道是否有更好的方法来仅在正确生成+上传 pdf 的情况下创建记录。而不是手动销毁它们并在发生错误时恢复对数据库的任何其他更新。