One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

الجزء 1: ما High Availability حقاً كانت تعني لـ NovaCommerce

منصة امتحان خيالية بأرقام حقيقية: خادم frontend واحد، اثنين backend ثابتين، عقدة قاعدة بيانات واحد و standbys اثنين مطفأين. يغطي الجزء تعريف high availability و الاختناقات الأولى التي احتفظت بالتحرك.

Author

Anichur Rahaman

منذ أسبوع13 min read3 views
الجزء 1: ما High Availability حقاً كانت تعني لـ NovaCommerce

في 25 سبتمبر، أثناء الامتحانات، بدأ الطلاب يرون HTTP 429، «طلبات كثيرة جداً». السبب لم يكن نقصاً في الخوادم، بل قاعدة تعد الشيء الخاطئ: محدِّد المعدل كان يعد حسب عنوان IP، والمدرسة تضع مئات الطلاب خلف عنوان واحد. أمام محدِّد المعدل، بناء كامل بدا كشخص واحد صبور جداً.

بعد أحد عشر يوماً، الساعة 02:28 من 6 أكتوبر، اختبار تحميل backend أرجع 2,854 خطأ تطبيق. كل واحد يقول نفس الشيء: انتهت مهلة انتظار الاتصال بـ Valkey. طبقة مختلفة، سبب مختلف، إصلاح مختلف. بعد ذلك جاءت CPU عادية، وهي المشكلة الوحيدة التي يحلها المزيد من الخوادم فعلاً.

هذا التسلسل هو القصة الحقيقية لهذا المشروع: الاختناق ظل يتحرك. إضافة خوادم ليست معمارية. إنها حركة واحدة بين عدة حركات، والأخيرة.

بين أواخر سبتمبر و 7 أكتوبر 2026، نقلنا منصة Laravel و Next.js على DigitalOcean من خادم frontend واحد وخادمي backend ثابتين إلى إعداد جاهز لذروة امتحان مجدول. هذا الجزء الأول يغطي نقطة البداية: المشكلة التجارية، ما ورثناه، لماذا لم يكن كافياً، ماذا كانت «التوفرية العالية» تعني لنا، وماذا أخبرتنا الاختبارات الأولى.

قبل أن نبدأ: NovaCommerce اسم خيالي، مستخدم لهذه الدراسة الحالة. حذفت أي شيء قد يميز الشركة أو خوادمها أو طلابها.

هذا الجزء الأول من سلسلة خماسية «من خادم واحد إلى جاهز لليلة الامتحان». NovaCommerce اسم خيالي؛ المعمارية والأرقام والأخطاء حقيقية.

منصة يصل حركتها على جدول زمني

NovaCommerce منصة تبيع الدورات وتدير امتحانات تفاعلية مجدولة للطلاب. يسجلون ويدفعون ويأخذون امتحانات حية ويرون نتائجهم ويستخدمون مساعدة الذكاء الاصطناعي للدراسة.

معظم حركة الويب تشبه تلة: تصعد في الصباح وتنزل في الليل. حركة الامتحانات جرف. تصور أبواب قاعة حفلات موسيقية. لا شيء يحدث لساعات، ثم تفتح الأبواب والجميع يصلون معاً. عندما يبدأ امتحان مجدول، آلاف الطلاب يضربون المنصة في نفس الدقيقة.

الخبر الجيد أن الجرف له جدول زمني. نعرف متى سيأتي. وصفت هذا الميزة بشكل عام في الدليل إلى auto-scaling للطفرات المرورية. هذه السلسلة هي النصف الآخر: منصة حقيقية، بأرقام حقيقية.

أعطتنا الأعمال أربعة متطلبات، بلغة واضحة.

  1. طفرات الامتحانات مجدولة وحادة. آلاف الطلاب يصلون في نفس الدقيقة.
  2. لا يجب فقدان الإرسالات. صفحة بطيئة محبطة. فقدان إرسال امتحان هو عمل الطالب الضائع.
  3. النشرات يجب أن تكون غير مرئية. الموقع يجب أن يبقى نشطاً بينما نشحن إصدارة.
  4. التكلفة يجب أن تبقى معقولة. الطاقة التي لا أحد يحتاجها تعتبر عيب أيضاً.

كل اختيار تقني في هذه السلسلة يعود لواحد من هذه الأسطر الأربعة.

نقطة البداية: ما ورثناه

إليك الإعداد كما وجدناه قبل 5 أكتوبر 2026، في منطقة DigitalOcean واحدة وشبكة خاصة واحدة.

رسم بياني للإعداد الأصلي: الطلاب يصلون إلى droplet frontend واحد، ثم load balancer backend يستهدف droplets backend ثابتين برقم معرف، ثم عقدة MySQL واحدة و Valkey وعامل؛ droplets standby مطفأة تجلس بعيداً، لا تزال مفروضة الرسوم
نقطتان من الفشل المفرد (frontend والقاعدة)، قائمة ثابتة من backend اثنين، و droplets standby اثنين مطفأين لكن لا تزال مفروضة عليها رسوم.

frontend: خادم واحد، ثمانية أنوية، أمين صندوق واحد

frontend كان droplet واحد بـ 8 vCPU و 16 GB من الذاكرة، يشغل حاوية Next.js واحدة. النطاق أشار مباشرة إليه، و TLS جاء من certbot على droplet نفسه. لا يوجد load balancer. إذا مات هذا الخادم، الموقع كان ميتاً.

كان يهدر الأموال أيضاً. عملية Node.js واحدة تقدِّم على أساس core واحد تقريباً، لذا معظم الـ vCPU الثمانية كانت خاملة: متجر به ثمانية ستة دفع وأمين صندوق واحد.

backend: خادمان لا يستطيع load balancer النمو معهما

DigitalOcean load balancer استهدف droplets backend ثابتين، كل واحد بـ 4 vCPU و 8 GB. التطبيق Laravel (PHP-FPM) خلف nginx في Docker، كحاويتين لكل عقدة: web لـ nginx و app لـ php-fpm. الطوابير (Horizon)، المجدول و Reverb، خادم WebSockets، تشغلوا على عامل بدلاً من ذلك.

استهدف load balancer كل backend برقم معرف خاص به ثابت، ليس بوسم. هذا الفرق بين قائمة ضيوف بها أسماء وقاعدة تقول «أي شخص يرتدي شارة طاقم العمل». مع الأسماء، الخادم الجديد لا يستطيع أن يدخل من تلقاء نفسه. يجب على شخص ما أن يعدّل القائمة.

كانت أخبار جيدة هنا، وأهميتها تفوق أي شيء آخر. عقد backend الويب كانت بالفعل بدون حالة. الجلسات والذاكرة المؤقتة والطوابير تعيش في Valkey المُدار، والسجلات تذهب إلى stderr. خادم يمكنك حذفه دون فقدان أي شيء هو خادم يمكنك ضربه. بدون هذه الخاصية بقية السلسلة لما كانت حدثت.

قاعدة البيانات و standbys المطفأة

كانت قاعدة البيانات عقدة Managed MySQL «Advanced» واحدة بـ 8 vCPU و 32 GB: عقدة واحدة، بدون standby.

ثم كانت droplets «standby» اثنين، محفوظة كنسخة احتياطية. كانت مطفأة. droplet مطفأ لا يزال يكلف أموالاً، وإعادة تشغيل أحدها تستغرق دقائق. إنها عجلة احتياطية في مرآب مقفل عبر المدينة: موجودة، تدفع عنها، وهي لا تساعد بينما أنت عالق في الطريق. هذا ليس توفراً عالياً.

شيئان أصغر جلسا في الزاوية. شهادات TLS للـ load balancer كانت تحميلات يدوية بتاريخ انتهاء صلاحية، لذا التجديد كان مهمة متكررة على شخص أن يتذكرها. وحد الـ droplet للحساب كان 25، وهذا سيهم مرة يستطيع فيها بركان اثنان النمو واستبدال العقد (الجزء 2).

لماذا هذا لم يكن كافياً

جزء الإعدادكيف بُنيما يحدث تحت الضغط أو الفشل
FrontendDroplet واحد 8 vCPU / 16 GB، حاوية Next.js واحدة، لا load balancerالموقع ميت إذا مات؛ معظم الأنوية خاملة، لأن عملية Node واحدة تستخدم تقريباً core واحد
BackendDroplets ثابتة اثنين 4 vCPU / 8 GB خلف load balancer، مستهدفة برقم معرفلا نمو تلقائي؛ الخادم الجديد لا يستطيع أن ينضم بنفسه
قاعدة البياناتعقدة Managed MySQL واحدة، 8 vCPU / 32 GB، بدون standbyلا توجد عقدة ثانية جاهزة للاستيلاء إذا فشلت العقدة
StandbysDroplets اثنين، مطفأةمفروضة عليها رسوم بينما مطفأة؛ دقائق للتشغيل

انظر إلى ما غائب من تلك الجدول: خطأ برمجي. لا شيء كان مكسوراً. الإعداد فعل ما بُني فعله. المشكلة كانت عدم توافق بين جرف مجدول وإعداد لم يستطع فقدان خادم، لم يستطع إضافة واحد بنفسه، واحتفظ بطاقته الاحتياطية مطفأة. لا تستطيع إصلاح عدم توافق. يجب أن تغيّر شكل النظام، وهذا ما تعنيه المعمارية.

ما كانت «التوفرية العالية» تعني حقاً هنا

«التوفرية العالية» واحدة من تلك العبارات التي يومئ بها الجميع ولا أحد يعرّفها. قبل أن نغيّر أي شيء، كتبنا ما تعنيه لهذه المنصة. انحصرت في أربعة أسطر.

لا خادم واحد يأخذ الموقع تحته. قاعدة البيانات تنجو من فقدان عقدة. النشرات والتقليص غير مرئية للطلاب. الطاقة جاهزة قبل امتحان يبدأ، وليس خمس دقائق بعده.

أحب تعريف يمكنك اختباره بسؤال. هل يمكننا سحب القابس على أي خادم واحد والاستمرار في الخدمة؟ هل يمكن لعقدة قاعدة بيانات أن تفشل بدون توقف الامتحان؟ هل يمكننا شحن إصدارة بدون لاحظ الطالب؟ هل الطاقة موجودة بالفعل عندما ينقر الطالب الأول «ابدأ»؟

قاعدتان تجاريتان تجلس فوقه: الإرسالات لا تُفقد أبداً، والتكلفة تبقى معقولة. الشكل يخطّط كل متطلب إلى الطبقة التي يجب أن تسلمه ولجزء السلسلة الذي يغطيه.

خمسة متطلبات مخطط لها إلى الطبقة التي تسلم كل واحد وجزء السلسلة الذي يغطيه: load balancers وبرك (الجزء 2)، managed MySQL مع standby (الجزء 3)، فحوصات صحة وrollouts محروسة (الأجزاء 2 و 4)، pre-scaling (الأجزاء 2 و 5)، عقد بدون حالة (الجزء 4)، إضافة قاعدة تكلفة
كل متطلب له صاحب: طبقة واحدة يجب أن تسلمه، وجزء واحد من هذه السلسلة يوضح كيفية القيام به.

السطر الأخير من التعريف، «وليس خمس دقائق بعده»، هو الذي ألم. في اختباراتنا، autoscaling أضاف العقدة الإضافية الأولى حول سبع دقائق بعد وصول CPU إلى 99%. سبع دقائق وقت طويل عندما يبدأ امتحان في واحدة. الطاقة يجب أن تكون موجودة قبل الجرف، وليس بعده.

الاختبارات الأولى: الاختناق ظل يتحرك

مع التعريف مكتوب، فعلنا ما تتطلبه الطريقة: قِس أولاً. بين أواخر سبتمبر و 6 أكتوبر راقبنا الإنتاج وأجرينا اختبارات تحميل، ووجدنا ثلاث مشاكل متتالية. تبدو غير مرتبطة، وهذا هو المقصود.

جدول زمني لثلاث اختناقات: محدِّد معدل لكل IP مصحح في الكود، Valkey timeouts الاتصال مصححة بتغيير حجم طبقة البيانات، و backend CPU محلول بطاقة
ثلاث اختناقات متتالية، كل واحد بنوع مختلف من الإصلاح. فقط الثالث يُحل بإضافة خوادم.

1. محدِّد المعدل: مشكلة كود (25 سبتمبر)

مقاس على الإنتاج: الطلاب كانوا يحصلون على HTTP 429 أثناء الامتحانات. محدِّد المعدل الشامل للـ API مفتاح على IP، لأن حارس المصادقة الافتراضي كان فارغاً للطلاب مستند على token، لذا محدِّد المعدل لم يستطيع معرفة من كان الطالب. مدرسة أو شركة نقل الهاتف تضع مئات الطلاب خلف عنوان NAT واحد، والبناء كله شارك جرة واحدة.

Status: Implemented. مفتاحنا محدِّد المعدل على طالب أو حساب معلم، و IP فقط للضيوف. عم قريب لهذا الخطأ عاد في 7 أكتوبر في throttles مستوى الطريق، وتلك القصة في الجزء 4.

2. اتصالات Valkey: مشكلة تحجيم طبقة البيانات (6 أكتوبر)

الساعة 02:28 من 6 أكتوبر، اختبار تحميل backend أرجع 2,854 خطأ 500 تطبيق. كل واحد كان RedisException: Operation timed out أثناء الاتصال، مع timeout اتصال 5 ثوان. Valkey (4 GB، primary زائد standby) لم يستطع قبول اتصالات سريعة بما يكفي تحت الحمل.

كيفية استخدام التطبيق لها أضافت إلى الضغط. كل طلب فتح اتصال TLS جديد إلى Valkey (حول 5.4 ملسة من CPU لكل طلب) وآخر إلى MySQL (حول 3.3 ملسة). تصور متجراً حيث كل عميل يُفحص في الباب، في كل مرة واحدة. تحت حشد، الباب يصبح الطابور.

Status: Implemented, then Tested. عدنا حجم Valkey في المكان إلى 8 GB مع عقدتين (primary و standby)، البيانات احتفظت والخادم لم يتغير. بعدها أعدنا الاختبار مع ارتقاء تدريجي من 25 إلى 500 طلب في الثانية، بدء على عقدتي backend بينما يتسع البركة تحت الحمل: 0 أخطاء تطبيق و 0 backend 5xx. كانت هناك 143 خطأ في load balancer، لكن فقط بينما الأنوية كانت مشبعة CPU. هذا الإشارة المتوقعة «خارج الطاقة»، ليس خطأ. طبقة البيانات تأخذ الجزء 3، و Valkey عود في الجزء 4. للنسخة الشاملة، انظر طبقة البيانات تحت الحمل.

3. Backend CPU: مشكلة طاقة

مع الأول اثنين ثابتين، ما بقي حساب عادي. عقدة backend واحدة 4 vCPU / 8 GB تخدم حول 65 إلى 70 طلب في الثانية من خليط API الحقيقي في CPU كامل، حول 59 ملسة من CPU لكل طلب. كل قرار طاقة لاحق مبني على هذا الرقم.

هذا واحد فقط من الثلاثة اختناق أن أكثر خوادم حقاً يحلون، وحتى هنا التوقيت هو الفخ: العقدة الإضافية الأولى جاءت حول سبع دقائق بعد وصول CPU إلى 99%. كيفية جعل الطاقة تصل في وقت مبكر هو موضوع الجزء 2.

لذا الترتيب كان محدِّد معدل (الكود)، ثم Valkey اتصالات (تحجيم طبقة البيانات)، ثم backend CPU (طاقة). إذا كنا قد ابتدأنا بإضافة خوادم، محدِّد المعدل كان قد قال 429 و Valkey كان قد لا يزال timeout.

ما الإعداد القديم كلّف

قبل الحديث عن الشكل الجديد، يساعد أن نعرف ما كلّف الشكل القديم. هذه أسعار DigitalOcean القائمة الشهرية للـ web tier القديم.

البندالتكلفةما اشترته
Web tier القديم: frontend، اثنين backend، worker، load balancer واحد، standbys اثنينحول $416 شهرياًموقع نشط مع عدة نقاط فشل مفردة
Droplets standby اثنين مطفأين$112 شهرياًلا توفرية: مفروضة عليها رسوم بينما مطفأة، دقائق للتشغيل
عقدة backend إضافية واحدة، للمقارنةحول $0.08 في الساعةطاقة تخدم الطلبات فعلاً

الـ $112، قليل أكثر من ربع web tier، هو الرقم الذي بقي معي. اشترى خادمين لا يستطيعان خدمة طلب واحد. Pre-scaling عقدة backend إضافية تكلّف حول $0.08 في الساعة، لذا رفع الطاقة لمساء يكلّف سنتات. الدفع طول الشهر عن طاقة مطفأة هو الطريقة الأكثر تكلفة للشعور بالأمان.

ملاحظة صادقة: الـ $416 تغطي فقط web tier، وليس قواعد البيانات أو Valkey أو storage، لذا لا يمكن مقارنتها مع فاتورة كاملة. في الجزء 5 أقارن مثل مع مثل.

الطريقة، والخطة للسلسلة

النموذج الذي كررناه طول المشروع هو ما رأيته للتو: قِس، جد الاختناق، افهم السبب، غيّر المعمارية، اختبر ثانية، وقِس ثانية. إضافة خوادم ليست معمارية. المعمارية اختيار طبقة تتغير، ولماذا.

إليك كيفية ملاءمة الخمسة أجزاء.

الجزءالطبقةما ستراه
1 (هذا الجزء)نقطة البدايةالمشكلة التجارية، الإعداد القديم، تعريف التوفرية العالية، الاختناقات الأولى والتكلفة القديمة
2طبقة التطبيقاثنين load balancers، اثنين autoscale pools، pre-scaling، image-based deploys ودرس rollout
3قاعدة البياناتمن عقدة MySQL واحدة إلى primary مع standby في نافذة 26 دقيقة، read/write split، PostgreSQL مع pgvector
4Valkey، workers، logsطوابير، worker، logging، monitoring وخطأ scale-in أثناء امتحان حقيقي
5الإثباتمساء امتحان حقيقي من 7 أكتوبر، load و soak testing، والمعمارية النهائية

فكّرنا أيضاً في أفكار لم نبنِها، وأنا أصنّفها. load balancer واحد لكلا الطبقتين كان Considered، بعدها Rejected. «warm standby جاهز في ثوان» كان Considered، لكن autoscale pools لا دافئة، لذا استخدمنا طاقة احتياطية تخدم بالفعل زائد pre-scaling. Kubernetes كان Considered للاحقة وليس implemented.

ليس كل شيء منته، وسأقول ذلك. عامل واحد مفرد هو نقطة فشل مفردة معروفة؛ إصلاحه Planned، وليس done (الجزء 4). اختبار soak رسمي متعدد الساعات أيضاً Planned (الجزء 5).

ما أردنا تحقيقه

الهدف كقائمة تحقق، مع الجزء حيث يُبنى أو يُختبر كل سطر.

  • Frontend و backend tiers خلف load balancers، مع اثنين على الأقل عقدة كل واحد (الجزء 2).
  • خوادم جديدة تنضم وتغادر بنفسها، بدون عقد محررة يدويّاً (الجزء 2).
  • طاقة مرفوعة قبل امتحان، مع autoscaling كالشبكة الأمان (الأجزاء 2 و 5).
  • قاعدة بيانات مع standby تكلّف أقل في الشهر من عقدة واحدة كبيرة (الجزء 3).
  • عقد ويب بدون حالة محلية، لذا الإرسالات تنجو من موت أي عقدة واحدة (الجزء 4).
  • Deploys و scale-in التي الطلاب لا يرونها (الأجزاء 2 و 4).
  • نموذج طاقة مبني من بيانات امتحان حقيقية (الجزء 5).
  • فاتورة يمكننا شرحها سطراً بسطراً (الجزء 5).

ما تعلمنا

  • اكتب «التوفرية العالية» بشروط الأعطال والأحداث قبل شراء أي شيء. تعريف يمكنك اختباره بسؤال أفضل من شعار.
  • Standby مطفأ تكلفة، وليس توفرية. لنا كان $112 شهرياً بدون حماية حقيقية.
  • الاختناق يتحرك. إصلاح الأول يكشف التالي، لذا قِس ثانية بعد كل تغيير.
  • اختناقات مختلفة تحتاج إصلاحات مختلفة: كود (محدِّد المعدل)، تحجيم طبقة البيانات (Valkey) وطاقة (backend CPU). فقط الأخير يُحل بـ خوادم أكثر.
  • عقد بدون حالة هي سعر الدخول. بدونها، الخطوات اللاحقة ما كانت آمنة.

بعد ذلك، في الجزء 2، نبني طبقة التطبيق: اثنين load balancers، اثنين autoscale pools، pre-scaling، وطريقة rollout تعلمت من blip 503 قصير.

About the Author

Anichur Rahaman

Continue Reading