الأولوية/الشدة:
عالية للنسخ المستضافة ذاتيًا التي تستخدم تخزين نسخ احتياطية متوافق مع 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 المُهيأ.
خطوات التكرار:
قم بتكوين تخزين النسخ الاحتياطي لـ Cloudflare R2 باستخدام مسار النسخ الاحتياطي المتوافق مع S3.
استخدم أرشيف نسخ احتياطي أكبر من عتبة التحميل متعدد الأجزاء.
قم بتشغيل نسخة احتياطية يدوية أو مجدولة.
راقب الفشل أثناء “جاري تحميل الأرشيف…”.
الإعدادات ذات الصلة:
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:
I’m facing issues with backing up my Discourse instance, the instance is setup to upload to Backblaze’s B2.
Error log:
Making sure archive does not already exist...
Creating empty archive...
Archiving data dump...
Archiving uploads...
Skipping uploads stored on S3.
Removing tmp '/var/www/discourse/tmp/backups/default/2026-01-16-151337' directory...
Gzipping archive, this may take a while...
Uploading archive...
EXCEPTION: failed to abort multipart upload: SSL_read: unexpected eof while reading…
أيضًا، تبدو مدخلات سجل التغييرات لـ aws-sdk-s3 بعد الإصدار 1.182.0 ذات صلة:
1.201.0: إصلاح التحميل متعدد الأجزاء لاحترام وضع 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
pfaffman
(Jay Pfaffman)
2 أبريل 2026، 2:49م
2
لم ألقِ نظرة على هذا الأمر منذ فترة، لكن إليك الطريقة التي تعاملت بها مع هذه المشكلة في الماضي:
لكنني فعلت ذلك لموقع يستخدم Backblaze. لقد أنشأت قالبًا وضعته في /root/aws-revert-template.yml مع هذا:
# هذا القالب يعيد إصدار aws-sdk-s3 إلى إصدار يعمل مع backblaze
params:
home: /var/www/discourse
hooks:
after_bundle_exec:
- exec:
cd: $home
cmd:
- 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
- bun…
لا يعتبرون ذلك خطأً في المساهمة (Contribute > Bug ) لأنهم لا يدّعون دعم كل خدمة 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)
amiantos
(Brad Root)
4 أبريل 2026، 7:30ص
4
تصحيح… لقد نجح هذا الحل البديل معي أيضًا، كان عليّ فقط وضعه في كتلة hooks: خاصة به وعدم إضافته إلى كتلة hooks: موجودة، يا له من غباء مني.
إعجاب واحد (1)