Создание резервных копий в Cloudflare R2 не удаётся при multipart-загрузке с aws-sdk-s3 1.182.0 (undefined method 'downcase' for nil)

Приоритет/Серьезность:

Высокий для самохостинговых инстансов, использующих хранилище резервных копий, совместимое с S3, поскольку запланированные резервные копии не удаются после создания архива.

Платформа:

Самохостинговый Discourse на последней ветке.

Текущая рабочая версия при наблюдении: v2026.4.0-latest

Ruby: 3.4.0

aws-sdk-s3: 1.182.0

Описание:

Резервное копирование в Cloudflare R2 завершается ошибкой на финальном этапе «Загрузка архива…».

Создание дамп базы данных и локального архива проходит успешно, но загрузка multipart-архива готового резервного копирования завершается ошибкой.

Фактический результат:

Резервное копирование завершается ошибкой с сообщением:

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. Настройте хранилище резервных копий на Cloudflare R2, используя путь к резервному копированию, совместимый с S3.
  2. Используйте архив резервной копии, превышающий порог multipart-загрузки.
  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 в пути multipart-загрузки.

Дополнительный контекст:

Существует похожая недавняя тема на Meta для Backblaze B2:

Кроме того, записи в changelog aws-sdk-s3 после версии 1.182.0 кажутся актуальными:

  • 1.201.0: Исправлена multipart-загрузка для учета режима request_checksum_calculation WHEN_REQUIRED
  • 1.210.2: При использовании пользовательских конечных точек или провайдеров конечных точек для операций PutObject и UploadPart выполняется переход к заголовочным контрольным суммам запросов

Discourse main по-прежнему, судя по всему, фиксирует версию aws-sdk-s3 на 1.182.0:

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

Я давно не смотрел на это, но вот как я решал эту проблему в прошлом:

Они не считают это #багом, так как не заявляют о поддержке каждого не совсем совместимого с S3 сервиса на планете.

Я не помню точно, для каких именно сайтов я это делал, но не припоминаю, чтобы что-то менял в этом недавно, поэтому думаю, что это всё ещё «лучшее» обходное решение.

Кажется, были ещё темы на эту тему, когда AWS выпустила новую библиотеку, которая сломала решения всех остальных.

2 лайка

Спасибо, это помогло мне.

Я применил обходной путь напрямую в app.yml через after_bundle_exec, зафиксировал версии aws-sdk-s3 на 1.177.0 и aws-sdk-core на 3.215, а затем пересобрал контейнер. После этого ручные резервные копии в Cloudflare R2 снова стали создаваться успешно, и ранее не работавшие загрузки через браузер также начали функционировать.

В моём случае ошибки проявлялись в виде multipart upload failed: undefined method 'downcase' for nil при использовании aws-sdk-s3 1.182.0.

Благодарю за обходное решение.

1 лайк

Исправление… этот обходной путь тоже сработал для меня, мне просто нужно было поместить его в отдельный блок hooks:, а не добавлять в существующий блок hooks. Какая же я глупая.

1 лайк