تواجه شركات إنتاج الفعاليات مشكلة ملفات غريبة بعض الشيء. يصوّر عدة مصورين الفعالية نفسها، وينتهي كل منهم بملفات حجمها 50 إلى 80 جيجابايت. يرفعونها عبر واي فاي القاعات المتقطع، أو من غرفة الفندق صباح اليوم التالي. ثم على فريق الإنتاج أن يسلّم كل شيء إلى العميل، الذي يريد رابطًا واحدًا، وغالبًا ملف ZIP واحدًا.
بنينا StageStore لهذا الغرض. نقل حتى الآن نحو 1,600 ملف، أي ما يزيد قليلًا على 1 تيرابايت، عبر 26 فعالية. تشرح هذه المقالة كيف رُكّب النظام ولماذا، بما في ذلك الأجزاء التي لم تنجح من المرة الأولى.
قاعدة التصميم: الملفات لا تمرّ أبدًا عبر خوادمنا
أهم قرار هو الأبسط. التطبيق لا يلمس بايتات الملفات إطلاقًا. يقرر خادمنا من يحق له الرفع أو التنزيل، ثم يتحدث المتصفح مباشرة مع التخزين باستخدام روابط موقّعة قصيرة العمر.
وهذا يزيل فئات كاملة من المشكلات:
- لا حدّ لحجم الطلب أو مدته عند رفع ملف حجمه 80 جيجابايت.
- لا عرض نطاق على الخادم ندفع ثمنه أو يمتلئ.
- يمكن تقسيم الرفع إلى أجزاء واستئنافه، لأن طبقة التخزين تدعم الرفع متعدد الأجزاء أصلًا.
البنية التقنية
يعمل كل شيء على منصة واحدة بدون خوادم (serverless)، تحت حساب واحد:
| المكوّن | التقنية | الدور |
|---|---|---|
| التطبيق | Next.js على حوسبة حافة بدون خوادم | الصفحات ومسارات API وفحص الصلاحيات |
| قاعدة البيانات | SQLite بدون خوادم | المؤسسات والفعاليات والروابط وسجلات الملفات |
| الملفات | تخزين كائنات متوافق مع S3 | الملفات المرفوعة وملفات ZIP المبنيّة |
| المهام الخلفية | سير عمل متين (durable workflows) | بناء ملفات ZIP |
| الجدولة | مشغّلات cron | قياس التخزين ليلًا |
وهناك خياران يستحقان الشرح.
تخزين بلا رسوم تنزيل. في هذا المجال يُنزَّل المحتوى النهائي نفسه الذي يبلغ 100 جيجابايت مرارًا من العملاء، ولو فُرضت رسوم على كل جيجابايت يُنزَّل لكان ذلك أكبر بند في الفاتورة.
SQL خام دون ORM. بدأنا بـ Prisma ولم ننجح في جعل محرك الاستعلامات لديه يُحمَّل بموثوقية في بيئة التشغيل بدون خوادم. المخطط صغير، لذا فإن SQL المباشر عبر الربط الأصلي أبسط وأوضح على أي حال.
رفع يصمد أمام الواي فاي السيئ
يفتح المصورون رابطًا، دون حساب، ويسحبون ملفاتهم إليه. وفي الخلفية:
- كل ملف رفع متعدد الأجزاء مستقل، فلا تعطّل الملفات المتوازية بعضها.
- يتناسب حجم الجزء مع حجم الملف، فتبقى حتى الملفات الضخمة تحت حد 10,000 جزء في الرفع الواحد.
- يحتفظ المتصفح بحالة الرفع الجارية محليًا (IndexedDB)، فيستعيد التحديث أو الانهيار قائمة الانتظار.
- عند الاستئناف يسأل العميل التخزين عن الأجزاء التي وصلت، ويرفع المفقود منها فقط.
انقطاع الاتصال أو إغلاق الحاسوب لا يعيد أي ملف إلى الصفر. ونعتقد أن هذه أهم ميزة في نظر من يصوّرون.
تنزيلات ZIP: الجزء الصعب
يريد العملاء ملف ZIP واحدًا. وملف ZIP بحجم 100 جيجابايت من الفيديو لا يمكن بناؤه في طلب واحد، ولا على جهاز واحد في وقت معقول. هذه هي طريقتنا.
احسب التخطيط مسبقًا
الفيديو والصور لا تُضغط، لذلك تُخزَّن المدخلات كما هي (stored) دون ضغط. وهذا يعني أن موقع كل بايت في الأرشيف النهائي معروف قبل قراءة أي بيانات. نستخدم ZIP64 في كل شيء، لأن الملفات والأرشيفات التي تتجاوز 4 جيجابايت أمر معتاد هنا.
القيمة الوحيدة غير المعروفة مسبقًا هي المجموع الاختباري CRC-32 لكل ملف.
قسّم العمل، ثم ادمج المجاميع الاختبارية
يُكتب الأرشيف كرفع متعدد الأجزاء إلى التخزين بأجزاء حجمها 40 MiB. يقسّم منسّق هذه الأجزاء إلى كتل، ويبني كل كتلةً أحدُ ما يصل إلى 64 عاملًا بالتوازي، فيقرؤون نطاقات المصدر ويحسبون شرائح CRC ويرفعون الأجزاء.
قد يتوزع الملف الواحد على عدة عمّال، فلا يرى أي عامل بايتاته كلها. تُدمج مجاميع الشرائح باستخدام خوارزمية crc32_combine القياسية من zlib، والنتيجة CRC صحيح واحد للملف. أما الأجزاء القليلة التي تعتمد على المجاميع النهائية، كواصفات البيانات والدليل المركزي، فتكتبها في النهاية خطوات "مصلِّحة" منفصلة حين ينتهي جميع العمّال.
كتبنا كاتب ZIP64 وكود CRC بأيدينا دون أي مكتبة. وتحققنا من النتيجة مع unzip وpython zipfile وأداة Archive Utility في macOS، وهي متطابقة بايتًا ببايت سواء بناها عامل واحد أو 8 أو 64. يُبنى حدث بحجم 93 جيجابايت تقريبًا في 3 إلى 10 دقائق.
الموثوقية التي تعلّمناها في الإنتاج
كشف بناء أرشيفات بهذا الحجم أمورًا لا تظهر إلا على نطاق كبير:
- قراءات تتجمّد في منتصف الكائن. الحل: القراءة بنوافذ محدودة، وتصغير النافذة بعد أي توقف، وإعادة المحاولة ما دام هناك تقدم.
- عمليات كتابة تتجمّد. الحل: مؤقت مراقبة للتوقف على كل جزء.
- نسخ تموت بصمت. الحل: يتحقق المنسّق من حالة عمّاله بنفسه بدل أن يثق بهم.
- نشر نسخة جديدة في منتصف عملية بناء. الحل: تُخزَّن الخطة مع المهمة، فلا تتغير عملية بناء جارية من تحتها.
- ملف مصدر تالف. بعد محاولات متكررة يُعلَّم الملف ويُستبعد ويُعاد بناء الأرشيف بدونه. يرى العميل أي ملف جرى تخطيه، ويعرف الفريق أنه يجب طلب إعادة رفعه.
المبدأ وراء ذلك كله: يجب أن تكون الإخفاقات مرئية، لا صامتة. لا ينبغي أن يتسلّم العميل أرشيفًا يحوي ما هو أقل مما يظن بصمت.
الوصول دون حسابات
يستخدم المصورون والعملاء روابط بالرموز، دون تسجيل. ويسجّل الفريق الدخول برمز واتساب، وهو يناسب طريقة عمل الفرق في الإمارات أصلًا، مع خيار البريد وكلمة المرور. وتُعزل بيانات كل مؤسسة عن غيرها، ويظهر شعار كل مؤسسة على صفحات الرفع والمعرض الخاصة بها.
التسجيل العام محمي بفحص ضد الروبوتات وبالتحقق عبر رمز واتساب.
ما ننصح به أي فريق آخر
- أبقِ البايتات بعيدًا عن خادم تطبيقك. الروابط الموقّعة والرفع متعدد الأجزاء تحل معظم مشكلة الملفات الكبيرة بمفردها.
- اجعل كل شيء قابلًا للاستئناف وللإعادة دون أثر جانبي. الشبكات تفشل، وكذلك العمّال. صمّم لإعادة المحاولة من البداية.
- لا تثق بقراءة واحدة. عند مئات الجيجابايتات، لا تبقى أعطال التخزين النادرة نادرة.
- فضّل الإخفاق المرئي. علّم الملف التالف، وأخبر المستخدم، وواصل العمل.
StageStore من بناء SolidStack في الإمارات لفرق إنتاج الفعاليات. وإن أردت تجربته فزر files.solidstack.ae. وإن كانت لديك مشكلة مشابهة، كالملفات الكبيرة أو المهام الطويلة أو معمارية السحابة، تواصل معنا.