Les sauvegardes vers Cloudflare R2 échouent lors du téléversement multipart avec aws-sdk-s3 1.182.0 (méthode undefined 'downcase' pour nil)

Priorité/Sévérité :

Élevée pour les instances auto-hébergées utilisant un stockage de sauvegarde compatible S3, car les sauvegardes planifiées échouent après la création de l’archive.

Plateforme :

Discourse auto-hébergé sur la dernière branche.

Version en production actuelle lors de l’observation : v2026.4.0-latest

Ruby : 3.4.0

aws-sdk-s3 : 1.182.0

Description :

Les sauvegardes vers Cloudflare R2 échouent à l’étape finale « Téléchargement de l’archive… ».

Le dump de la base de données et la création de l’archive locale se déroulent avec succès, mais le téléchargement multipart de l’archive de sauvegarde terminée échoue.

Résultat réel :

La sauvegarde échoue avec :

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

La pile d’appels inclut :

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

Résultat attendu :

L’archive de sauvegarde devrait être téléchargée avec succès vers le magasin de sauvegarde compatible S3 configuré.

Étapes pour reproduire :

  1. Configurer le stockage de sauvegarde vers Cloudflare R2 en utilisant le chemin de sauvegarde compatible S3.
  2. Utiliser une archive de sauvegarde supérieure au seuil multipart.
  3. Lancer une sauvegarde manuelle ou planifiée.
  4. Constater l’échec durant « Téléchargement de l’archive… ».

Configuration pertinente :

  • 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

Extrait de la pile d’appels observée :

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

Cela suggère que checksum_algorithm est nil dans le chemin du téléchargement multipart.

Contexte supplémentaire :

Il existe un sujet Meta récent similaire concernant Backblaze B2 :

De plus, les entrées du journal des modifications (changelog) d’aws-sdk-s3 après la version 1.182.0 semblent pertinentes :

  • 1.201.0 : Correction du téléchargement multipart pour respecter le mode request_checksum_calculation when_required
  • 1.210.2 : Recours aux en-têtes de somme de contrôle de la requête lors de l’utilisation de points de terminaison personnalisés ou de fournisseurs de points de terminaison pour les opérations PutObject et UploadPart

Discourse main semble toujours verrouiller aws-sdk-s3 sur la version 1.182.0 :

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

Je n’ai pas regardé ça depuis un moment, mais voici comment j’ai géré cela par le passé :

Ils ne considèrent pas cela comme un Contribute > Bug parce qu’ils ne prétendent pas supporter chaque service S3 pas tout à fait compatible sur la planète.

Je ne me souviens plus exactement des sites pour lesquels je faisais cela, mais je ne me souviens pas avoir changé quoi que ce soit à ce sujet récemment, donc je pense que c’est toujours la « meilleure » solution de contournement.

Je pense qu’il y avait quelques autres sujets à ce sujet lorsque AWS a publié la nouvelle bibliothèque qui a cassé les offres des autres.

2 « J'aime »

Merci, cela a résolu le problème pour moi.

J’ai appliqué la solution de contournement directement dans app.yml via after_bundle_exec, en verrouillant aws-sdk-s3 à la version 1.177.0 et aws-sdk-core à la version 3.215, puis j’ai reconstruit le conteneur. Depuis, les sauvegardes manuelles vers Cloudflare R2 fonctionnent à nouveau, et les téléversements par navigateur qui échouaient précédemment fonctionnent également.

Dans mon cas, les erreurs s’affichaient sous la forme multipart upload failed: undefined method 'downcase' for nil avec aws-sdk-s3 1.182.0.

Je remercie pour cette solution de contournement.

1 « J'aime »

Correction… cette solution de contournement a fonctionné pour moi aussi, il fallait juste la placer dans son propre bloc hooks: et ne pas l’ajouter à un bloc hooks: existant, quelle bêtise de ma part.

1 « J'aime »