备份到 Cloudflare R2 在使用 aws-sdk-s3 1.182.0 进行多部分上传时失败(nil 没有 'downcase' 方法)

优先级/严重程度:

对于使用 S3 兼容备份存储的自托管实例,优先级为高,因为归档创建后计划备份会失败。

平台:

最新分支的自托管 Discourse。

观察时的当前运行版本:v2026.4.0-latest

Ruby: 3.4.0

aws-sdk-s3: 1.182.0

描述:

备份到 Cloudflare R2 在最后的“正在上传归档…”步骤失败。

数据库转储和本地归档创建均成功完成,但已完成备份归档的多部分上传失败。

实际结果:

备份失败,错误信息如下:

EXCEPTION: multipart upload failed: undefined method 'downcase' for nil

堆栈跟踪包含:

aws-sdk-s3-1.182.0/lib/aws-sdk-s3/multipart_file_uploader.rb
lib/backup_restore/s3_backup_store.rb:48
lib/backup_restore/creator.rb:434

预期结果:

备份归档应成功上传到配置的 S3 兼容备份存储。

可复现步骤:

  1. 使用 S3 兼容备份路径将备份存储配置为 Cloudflare R2。
  2. 使用大于多部分阈值的备份归档。
  3. 运行手动或计划备份。
  4. 观察“正在上传归档…”期间发生的失败。

相关配置:

  • DISCOURSE_BACKUP_LOCATION=s3
  • DISCOURSE_S3_ENDPOINT=https://.r2.cloudflarestorage.com
  • DISCOURSE_S3_FORCE_PATH_STYLE=true
  • DISCOURSE_S3_BACKUP_BUCKET=
  • AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED
  • AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED

观察到的堆栈跟踪片段:

algorithm = resp.context.params[:checksum_algorithm]
k = "checksum_#{algorithm.downcase}".to_sym

这表明在多部分上传路径中,checksum_algorithm 为 nil。

其他背景信息:

Meta 上有一个类似的关于 Backblaze B2 的近期主题:

此外,1.182.0 之后的 aws-sdk-s3 更新日志条目也与此相关:

  • 1.201.0:修复多部分上传以在需要时尊重 request_checksum_calculation 模式
  • 1.210.2:在使用自定义端点或端点提供程序进行 PutObject 和 UploadPart 操作时,回退到使用头部请求校验和

Discourse 主分支目前似乎仍将 aws-sdk-s3 锁定在 1.182.0:

https://raw.githubusercontent.com/discourse/discourse/main/Gemfile.lock

我有一段时间没看过这个了,但这是我过去处理这个问题的方法:

他们不认为这是一个 bug,因为他们并不声称支持地球上每一个与 S3 不完全兼容的服务。

我记不清具体是哪些网站了,但我不记得最近对此做过任何更改,所以我认为这仍然是“最佳”的变通方案。

我记得在 AWS 发布导致其他人服务失效的新库时,还有一些其他相关话题。

2 个赞

感谢,这为我解决了问题。

我通过 after_bundle_exec 直接在 app.yml 中应用了变通方案,将 aws-sdk-s3 锁定为 1.177.0,将 aws-sdk-core 锁定为 3.215,然后重新构建了容器。之后,手动备份到 Cloudflare R2 再次成功,之前失败的浏览器上传也恢复了正常。

在我的情况下,故障表现为在 aws-sdk-s3 1.182.0 上出现 multipart upload failed: undefined method 'downcase' for nil 错误。

感谢提供这个变通方案。

1 个赞

更正……这个变通方法对我有效,我只需要将它放在独立的 hooks: 块中,而不是添加到现有的 hooks 块里,真是我犯傻。