You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails事务中使用dry-monads触发ActiveStorage::FileNotFoundError问题

问题产生原因
  • 根本原因是ActiveStorage的附件写入与数据库事务的时序不匹配:file.attach执行时仅会在数据库中写入附件关联记录,实际的文件写入操作(本地磁盘或云存储)默认会延迟到数据库事务正式提交之后才触发。
  • 你在事务块内部调用了myobject.file.download,此时事务尚未提交,文件还没有被真正写入存储介质,直接读取自然会抛出ActiveStorage::FileNotFoundError。
  • 叠加dry-monads的执行逻辑:事务块内部的yield会在返回失败结果时直接中断执行、触发事务回滚,进一步导致附件写入操作永远不会被执行。
解决方法

方案1:拆分事务与文件操作(最推荐)

将核心业务数据的原子操作放到事务块内,文件读取、解析等非数据库操作移到事务提交之后执行:

# 第一步先创建核心业务记录
process = yield create_process
# 事务块仅保障数据库操作的原子性
Process.transaction do
  yield attach_file_to_process(process)
end
# 事务提交后再执行文件解析逻辑
yield parse_process_file(process)

# 拆分原create_file方法
def attach_file_to_process(process)
  process.file.attach(io: file['tempfile'], filename: file['filename'], content_type: file['type'])
  Success(process)
end

def parse_process_file(process)
  csv_content = CSV.parse(process.file.download.force_encoding('UTF-8'), headers: true)
  Success(csv_content)
end

方案2:预上传文件生成Blob

如果必须在事务内部校验文件内容,可以提前将文件上传生成ActiveStorage Blob,再在事务内关联到业务对象,此时可以直接从Blob读取内容,不需要等待事务提交:

# 提前上传文件生成Blob,不涉及事务操作
file_blob = ActiveStorage::Blob.create_and_upload!(
  io: file['tempfile'],
  filename: file['filename'],
  content_type: file['type']
)

Process.transaction do
  process = yield create_process
  process.file.attach(file_blob)
  # 直接从预生成的Blob读取内容
  csv_content = CSV.parse(file_blob.download.force_encoding('UTF-8'), headers: true)
  # 后续CSV业务校验逻辑
  yield validate_csv_content(csv_content)
end

方案3:使用事务回调执行文件操作

如果逻辑不允许拆分代码结构,可以通过after_commit回调在事务提交完成后再触发文件解析逻辑,需额外适配dry-monads的异常捕获和结果返回逻辑即可。

内容的提问来源于stack exchange,提问作者Petros Kalafatidis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:15:02