Falha no upload de backup devido a TransferManager não inicializado

Após atualizar para v2026.8.0-latest.1 +4, meus backups começaram a falhar. Estes são os logs: log.txt.zip (22,1 KB)

Em particular, aqui está a seção que parece relevante:

[2026-08-04 03:37:52] Enviando arquivo compactado...
[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'

Não tenho certeza de como resolver esse problema.

Atualizei para a v2026.9.0-latest +565, mas ainda encontrei exatamente o mesmo problema.

A versão do Discourse que você está executando inclui uma versão do aws-sdk-s3 que deve fornecer Aws::S3::TransferManager.

Você está usando a instalação oficial do Discourse via Docker? Se estiver usando o setup oficial do Docker, o contêiner foi totalmente recriado como parte da atualização?

Sim, acredito que estou usando a instalação oficial do Discourse via Docker. Existe alguma forma de eu ter certeza disso? Já faz um tempo desde que realizei a instalação.

Para atualizar, executei ./launcher rebuild app. Suponho que isso signifique que o container foi totalmente recriado, mas talvez eu esteja errado?

Alguns detalhes adicionais que podem ser relevantes:

  • Estou usando Ubuntu 24.04.4 LTS
  • O app.yml instala os plugins oficiais docker_manager e discourse-doc-categories no hook after_code. Não há outros plugins definidos no app.yml.
  • Estou tentando armazenar os backups em um armazenamento compatível com S3 fornecido pela Backblaze, e isso funcionava anteriormente.

Uma coisa que pode ajudar a estreitar a busca é verificar qual gem aws-sdk-s3 está sendo carregada de fato dentro do container em execução.

A alteração no Discourse que introduziu o Aws::S3::TransferManager também atualizou o aws-sdk-s3 da versão 1.182.0 para a 1.227.0:

Na revisão do Discourse que você vinculou, o Gemfile.lock ainda deve estar usando o aws-sdk-s3 1.227.0, que fornece o Aws::S3::TransferManager.

Você poderia tentar entrar no container:

cd /var/discourse
./launcher enter app

Em seguida, dentro do container:

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
'

Também pode ser útil verificar a própria aplicação sob o 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
'

Depois, saia do container com:

logout

Se isso reportar uma versão mais antiga do aws-sdk-s3, ou se o TransferManager estiver ausente, isso explicaria a exceção no backup e sugeriria que as gems instaladas no container não correspondem ao Gemfile.lock atual do Discourse.

Se reportar a versão 1.227.0 e resolver o Aws::S3::TransferManager com sucesso, então o problema é mais incomum e saberemos para onde olhar: o que difere entre o processo de backup do Sidekiq e aquele ambiente interativo.

Como você está usando ./launcher rebuild app, isso soa como o fluxo padrão do discourse_docker; os comandos acima devem nos dizer com mais precisão o que acabou ficando no container reconstruído.

Parece que eu tenho uma versão antiga do 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

Ao investigar isso, lembrei que eu usava este workaround para contornar um problema em que o AWS SDK não funcionava com o Backblaze. Ao me atualizar sobre o tópico, vi que o Backblaze atualizou sua API, o que torna o workaround desnecessário.

Depois que removi o workaround, o backup foi concluído com sucesso! Obrigado pela ajuda. :heart:

Uma reflexão a partir disso: modelos de compatibilidade temporários como este podem ser mais seguros se tiverem algum tipo de condição de autoexpiração/guarda, em vez de continuar sobrescrevendo as dependências do Discourse indefinidamente.

Por exemplo, com backports temporários de código-fonte, estou planejando usar hooks em produção que primeiro verifiquem se a correção upstream já está presente e apliquem o patch apenas se ainda for necessário.

Neste caso, algo ao longo dessas linhas poderia potencialmente ter impedido que o pin antigo aws-sdk-s3 1.177.0 continuasse uma vez que o próprio Discourse começou a depender de funcionalidades mais novas do SDK:

hooks:
  after_bundle_exec:
    - exec:
        cd: $home
        cmd:
          - |
            if grep -q "Aws::S3::TransferManager" lib/s3_helper.rb; then
              echo "Discourse agora requer Aws::S3::TransferManager; pulando o downgrade obsoleto do aws-sdk-s3 do Backblaze"
            else
              echo "Aplicando pin temporário de compatibilidade do aws-sdk-s3 do Backblaze"
              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

Essa verificação específica é apenas ilustrativa - uma verificação de versão mais geral ou uma falha explícita quando o Discourse passar da versão de SDK esperada provavelmente seria melhor.

Mesmo falhar de forma explícita seria provavelmente preferível a silenciosamente produzir um container cujos gems não correspondem mais às versões esperadas pelo Discourse.

Embora eu concorde que falhar de forma explícita teria sido desejável, o caso era que, anteriormente, eu queria que as gems não corresendessem às versões esperadas pelo Discourse, porque a versão mais recente do aws-sdk-s3 enviava cabeçalhos que o Backblaze não suportava e eu queria que os backups funcionassem.

Não sei se havia alguma verificação que eu poderia ter configurado proativamente para me notificar quando o contorno (workaround) deixasse de ser necessário. Implementei o contorno há um ano e meio, em abril de 2025, antes que uma correção estivesse disponível.

Acho que o melhor que eu poderia ter feito foi adicionar uma data de expiração, para me forçar a verificar se o contorno ainda era necessário. Uma funcionalidade assim seria bem-vinda. Ou talvez a responsabilidade seja minha de ser mais proativo e configurar lembretes para verificar se o contorno ainda é necessário. Ter algumas anotações me ajudou nesse aspecto.