因未初始化的 TransferManager 导致备份上传失败

在更新到 v2026.8.0-latest.1 +4 后,我的备份开始失败。以下是日志:log.txt.zip (22.1 KB)

特别是,以下是看起来相关的部分:

[2026-08-04 03:37:52] Uploading archive...
[2026-08-04 03:37:52] EXCEPTION: uninitialized constant Aws::S3::TransferManager
[2026-08-04 03:37:52] /var/www/discourse/lib/s3_helper.rb:453:in 'S3Helper#transfer_manager'
/var/www/discourse/lib/s3_helper.rb:332:in 'S3Helper#upload_file'
/var/www/discourse/lib/backup_restore/s3_backup_store.rb:48:in 'BackupRestore::S3BackupStore#upload_file'
/var/www/discourse/lib/backup_restore/creator.rb:437:in 'BackupRestore::Creator#upload_archive'
/var/www/discourse/lib/backup_restore/creator.rb:41:in 'BackupRestore::Creator#run'
/var/www/discourse/lib/backup_restore.rb:13:in 'BackupRestore.backup!'
/var/www/discourse/app/jobs/regular/create_backup.rb:10:in 'Jobs::CreateBackup#execute'
/var/www/discourse/app/jobs/base.rb:313:in 'block (2 levels) in Jobs::Base#perform'

我不确定如何解决这个问题。

我已经更新到了 v2026.9.0-latest +565,但依然遇到了完全相同的问题。

您正在运行的 Discourse 版本包含一个 aws-sdk-s3 版本,该版本应该提供 Aws::S3::TransferManager

您是否使用的是官方的 Discourse Docker 安装?如果您使用的是官方 Docker 配置,在升级过程中容器是否已完全重建?

是的,我认为我使用的是官方 Discourse Docker 安装方式。有没有什么方法可以让我确定这一点?距离我安装已经有一段时间了。

为了更新,我运行了 ./launcher rebuild app。我假设这意味着容器被完全重建了,但我可能理解错了?

以下是一些可能相关的详细信息:

  • 运行的是 Ubuntu 24.04.4 LTS
  • app.ymlafter_code 钩子中安装了官方的 docker_managerdiscourse-doc-categories 插件。app.yml 中没有定义其他插件。
  • 我正试图将备份存储到 Backblaze 提供的兼容 S3 的存储中,之前这是可以正常工作的。

缩小排查范围的一个方法可能是检查实际在运行中的容器内加载了哪个版本的 aws-sdk-s3 gem。

引入 Aws::S3::TransferManager 的 Discourse 更改同时将 aws-sdk-s3 从 1.182.0 升级到了 1.227.0:

在你链接的 Discourse 版本中,Gemfile.lock 应该仍然在使用 aws-sdk-s3 1.227.0,该版本确实提供了 Aws::S3::TransferManager

你能尝试进入容器吗:

cd /var/discourse
./launcher enter app

然后,在容器内部:

cd /var/www/discourse
bundle info aws-sdk-s3
bundle exec ruby -e '
require "aws-sdk-s3"
puts "gem: #{Gem.loaded_specs["aws-sdk-s3"]&.full_name}"
puts "path: #{Gem.loaded_specs["aws-sdk-s3"]&.full_gem_path}"
p Aws::S3.autoload?(:TransferManager)
p Aws::S3::TransferManager
'

在 Rails 环境下检查应用程序本身可能也有用:

RAILS_ENV=production bundle exec rails runner '
puts "aws-sdk-s3: #{Gem.loaded_specs["aws-sdk-s3"]&.version}"
p Aws::S3.autoload?(:TransferManager)
p Aws::S3::TransferManager
'

然后使用以下命令退出容器:

logout

如果报告的是旧版本的 aws-sdk-s3,或者缺少 TransferManager
这将解释备份异常,并表明容器中安装的 gem 与当前的 Discourse
Gemfile.lock 不匹配。

如果它报告的是 1.227.0 并成功解析了
Aws::S3::TransferManager,那么问题就不太寻常了,我们将知道需要查看 Sidekiq 备份进程与该交互式环境之间的差异。

既然你使用的是 ./launcher rebuild app,这听起来像是标准的 discourse_docker 工作流;上述命令应该能让我们更精确地了解重建后的容器中实际包含了什么。

看起来我使用的是旧版本的 aws-sdk-s3。

$ bundle info aws-sdk-s3
  * aws-sdk-s3 (1.177.0)
	Summary: AWS SDK for Ruby - Amazon S3
	Homepage: https://github.com/aws/aws-sdk-ruby
	Source Code: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sdk-s3
	Changelog: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sdk-s3/CHANGELOG.md
	Path: /var/www/discourse/vendor/bundle/ruby/3.4.0/gems/aws-sdk-s3-1.177.0

在调查此问题时,我想起了我曾用这个变通方法来绕过 AWS SDK 与 Backblaze 不兼容的问题。重新回顾该话题后,我发现Backblaze 已经更新了他们的 API,因此不再需要该变通方法。

移除变通方法后,备份成功了!感谢大家的帮助。:heart:

对此的一点想法:像这样的临时兼容性模板,如果带有一些自过期/守卫条件,可能会更安全,而不是一直无限期地覆盖 Discourse 的依赖项。

例如,对于临时的源代码回移(backports),我计划在生产环境中使用钩子,首先检查上游修复是否已经存在,仅在仍然需要时才应用补丁。

在这种情况下,类似以下内容的逻辑本可以防止旧的 aws-sdk-s3 1.177.0 版本锁定在 Discourse 自身开始依赖较新 SDK 功能后继续存在:

hooks:
  after_bundle_exec:
    - exec:
        cd: $home
        cmd:
          - |
            if grep -q "Aws::S3::TransferManager" lib/s3_helper.rb; then
              echo "Discourse 现在需要 Aws::S3::TransferManager;跳过已过时的 Backblaze aws-sdk-s3 降级"
            else
              echo "应用临时的 Backblaze aws-sdk-s3 兼容性版本锁定"
              bundle config set frozen false
              sed -i 's/gem "aws-sdk-s3", require: false/gem "aws-sdk-s3", "1.177.0", require: false/' Gemfile
              bundle update aws-sdk-s3
              bundle add aws-sdk-core --version 3.215
            fi

上述特定的检查仅作为示例——更通用的版本检查,或者当 Discourse 超过预期的 SDK 版本时显式失败,可能效果更好。

即使大声报错(failing loudly),也比静默生成一个 gem 版本与 Discourse 预期版本不匹配的容器要好。

虽然我同意“大声失败”(failing loudly)的做法确实不错,但在此之前,我其实是希望这些 gem 的版本与 Discourse 所期望的版本不匹配的。因为较新的 aws-sdk-s3 版本会发送 Backblaze 不支持的头部信息,而我希望备份功能能够正常工作。

我不确定是否有一种检查机制,可以让我提前设置,以便在工作变通方案不再必要时通知我。我在 2025 年 4 月,也就是在修复方案可用之前的一年半前,实施了这一变通方案。

我认为我所能做的最好的事情是添加一个过期日期,以强制自己检查该变通方案是否仍然必要。类似这样的功能可能会很有用。或者,也许责任在于我,我应该更主动地设置提醒,以检查该变通方案是否仍然必要。在这方面,我写下的一些笔记对我很有帮助。