ليلة إطلاق المنتج الجديد: “أبو عمر، الموقع وقع!”
كنت قاعد في أمان الله، بشرب كاسة الشاي بالنعناع بعد يوم شغل طويل، ومبسوط إنه أطلقنا الموسم الجديد لمتجر إلكتروني لأحد العملاء. كان الإطلاق متزامن مع حملة إعلانية كبيرة، والتوقعات كانت عالية. فجأة، برن التلفون… رقم مدير المشروع. قلبي نقزني.
“أبو عمر، شو هالحكي يا زلمة! الموقع وقع! الناس مش قادرة تفوت، والطلبات واقفة!”. نزلت عليّ الجملة زي الصاعقة. فتحت اللابتوب بسرعة، وإيدي بترجف. شفت بعيني رسالة الخطأ “503 Service Unavailable” تملأ الشاشة. حاولنا نعمل إعادة تشغيل للسيرفرات، واشتغل الموقع لدقائق… ثم انهار مرة أخرى.
قضينا الليلة كلها في حالة طوارئ، نحاول إطفاء الحرائق بينما رسائل العملاء الغاضبة تتراكم. كانت تجربة مُهينة ومُحبطة بكل معنى الكلمة. خسرنا مبيعات، وخسرنا ثقة، والأهم، خسرنا راحة بالنا. في تلك الليلة، أقسمت أن هذا الكابوس لن يتكرر. ومن هنا، بدأت رحلتي الحقيقية مع عالم اختبارات الأداء، وتحديداً، “اختبار الإجهاد” أو الـ Stress Testing.
ما هو اختبار الإجهاد (Stress Testing)؟ وليش هو طوق النجاة؟
ببساطة شديدة، تخيل إنك بتبني جسر. هل بتفتتحه للسيارات مباشرة؟ طبعاً لأ. أول إشي بتجيب أوزان ثقيلة، شاحنات محملة، وبتحطها فوق الجسر. بتزيد الوزن شوي شوي… كمان… وكمان… لحد ما تشوف وين أول نقطة ضعف رح تظهر. هل رح يميل؟ هل رح يظهر شرخ؟ هذا هو بالضبط اختبار الإجهاد.
في عالم البرمجيات، اختبار الإجهاد هو عملية دفع نظامك (موقعك، تطبيقك، واجهتك البرمجية API) إلى أقصى حدوده، وحتى أبعد من حدوده، لنرى كيف سيتصرف. الهدف مش بس نعرف “كم مستخدم بيتحمل”، الهدف الأهم هو نعرف “كيف ينهار؟” و “أين ينهار؟”.
الفرق بين اختبار الحمل (Load Testing) واختبار الإجهاد (Stress Testing):
– اختبار الحمل (Load Test): يتأكد أن النظام يعمل بشكل جيد تحت الحمل المتوقع (مثلاً 1000 مستخدم متزامن).
– اختبار الإجهاد (Stress Test): يزيد الحمل بشكل مستمر فوق المتوقع ليعرف نقطة الانهيار القصوى (مثلاً يبدأ بـ 1000 ويزيد حتى ينهار النظام عند 2500 مستخدم).
التشخيص: لماذا كان موقعنا ينهار؟
بعد ليلة الكارثة، قررنا نعمل تشريح كامل للمشكلة. ما بدنا حلول مؤقتة، بدنا نعرف السبب الجذري. الشكوك كانت تدور حول عدة متهمين كالعادة:
- قاعدة البيانات (Database): هل هناك استعلامات (Queries) بطيئة تستهلك كل الموارد؟
- الخادم (Server): هل المعالج (CPU) أو الذاكرة (RAM) تصل إلى 100% وتختنق؟
- الكود نفسه (Application Code): هل هناك تسريب في الذاكرة (Memory Leak) أو خوارزميات غير فعالة؟
- خدمات الطرف الثالث (Third-party APIs): هل واجهة برمجية خارجية نعتمد عليها لا تستجيب تحت الضغط؟
بدون بيانات حقيقية، كل هذا مجرد تخمين. وهنا جاء دور بطل قصتنا: اختبار الإجهاد.
كيف طبقنا اختبار الإجهاد خطوة بخطوة (الدليل العملي)
قررنا نستخدم أداة اسمها K6 (من Grafana Labs). ليش K6 بالذات؟ لأنها حديثة، سهلة، وبتستخدم لغة JavaScript لكتابة الاختبارات، وهي لغة مألوفة لأغلب مطوري الويب.
الخطوة الأولى: كتابة سيناريو الاختبار
أول شيء، لازم نكتب “سيناريو” يحاكي سلوك المستخدم الحقيقي. المستخدم ما بزور الصفحة الرئيسية وبس، هو بتصفح المنتجات، بضيف للسلة، وبروح لصفحة الدفع.
هذا مثال بسيط لسكريبت K6 كتبناه لمحاكاة هذا السلوك:
import http from 'k6/http';
import { sleep, check } from 'k6';
// 1. خيارات الاختبار: هنا نحدد عدد المستخدمين الافتراضيين ومدة الاختبار
export const options = {
stages: [
{ duration: '2m', target: 200 }, // زيادة تدريجية إلى 200 مستخدم خلال دقيقتين
{ duration: '5m', target: 200 }, // البقاء عند 200 مستخدم لمدة 5 دقائق
{ duration: '2m', target: 1000 }, // محاكاة وقت الذروة المفاجئ: زيادة إلى 1000 مستخدم خلال دقيقتين (Spike)
{ duration: '3m', target: 1000 }, // البقاء عند 1000 مستخدم
{ duration: '1m', target: 0 }, // تقليل الحمل للصفر
],
thresholds: {
'http_req_failed': ['rate<0.01'], // نسبة الطلبات الفاشلة يجب أن تكون أقل من 1%
'http_req_duration': ['p(95)<800'], // 95% من الطلبات يجب أن تتم في أقل من 800ms
},
};
// 2. السيناريو الرئيسي الذي سينفذه كل مستخدم افتراضي
export default function () {
// زيارة الصفحة الرئيسية
const res1 = http.get('https://my-test-site.com/');
check(res1, { 'Homepage was 200': (r) => r.status === 200 });
sleep(1);
// البحث عن منتج
const res2 = http.get('https://my-test-site.com/api/products?search=my-product');
check(res2, { 'Search API was 200': (r) => r.status === 200 });
sleep(1);
// إضافة المنتج للسلة
const payload = JSON.stringify({ productId: '12345', quantity: 1 });
const params = { headers: { 'Content-Type': 'application/json' } };
const res3 = http.post('https://my-test-site.com/api/cart', payload, params);
check(res3, { 'Add to cart was 200': (r) => r.status === 200 });
sleep(2);
}
هذا السكريبت يحاكي زيادة تدريجية في عدد المستخدمين، ثم فترة ثبات، ثم “قفزة” مفاجئة في الحمل، وهذا بالضبط ما يحدث في الحملات الإعلانية.
الخطوة الثانية: تشغيل الاختبار ومراقبة النتائج
قمنا بإنشاء بيئة اختبار (Staging Environment) مطابقة تماماً لبيئة الإنتاج. (نصيحة ذهبية: إياك أن تجري اختبار إجهاد على بيئة الإنتاج الحية إلا إذا كنت تعرف تماماً ماذا تفعل!).
شغلنا الاختبار من خلال سطر الأوامر:
k6 run stress-test.js
وبدأنا نراقب لوحة التحكم (Dashboard) الخاصة بالسيرفرات وقاعدة البيانات. في البداية، كل شيء كان تمام. لكن عندما وصل عدد المستخدمين الافتراضيين (VUs) إلى حوالي 450، بدأت الكارثة تتكرر أمام أعيننا، ولكن هذه المرة في بيئة آمنة:
- زمن الاستجابة (Request Duration): قفز من 200ms إلى 5000ms وأكثر.
- معدل الأخطاء (Failed Requests): بدأ بالارتفاع بشكل مخيف.
- استهلاك المعالج (CPU Usage): وصل إلى 100% على سيرفر قاعدة البيانات.
وجدنا الجاني! المشكلة كانت في قاعدة البيانات.
الخطوة الثالثة: الإصلاح وإعادة الاختبار
بعد تحديد المشكلة، أصبح الحل أسهل. وجدنا أن هناك استعلاماً معيناً (SQL Query) في صفحة المنتجات كان يقوم بعملية فحص كامل للجدول (Full Table Scan) في كل مرة يتم طلبه، وبدون استخدام الفهارس (Indexes).
الإصلاح الأول: تحسين قاعدة البيانات (Database Optimization)
قمنا بإضافة الفهرس المناسب للجدول. الفرق كان كالليل والنهار.
الإصلاح الثاني: التخزين المؤقت (Caching)
أضفنا طبقة تخزين مؤقت (Caching Layer) باستخدام Redis للبيانات التي لا تتغير كثيراً، مثل قائمة المنتجات الأكثر مبيعاً في الصفحة الرئيسية. هذا قلل الضغط على قاعدة البيانات بشكل هائل.
الإصلاح الثالث: التوسع الأفقي (Horizontal Scaling)
أدركنا أن خادماً واحداً لن يكون كافياً أبداً. قمنا بضبط البنية التحتية لاستخدام موازن أحمال (Load Balancer) مع مجموعة من الخوادم التي يمكن زيادتها تلقائياً (Auto-scaling Group) عند زيادة الضغط.
بعد كل إصلاح، كنا نعيد تشغيل اختبار الإجهاد لنرى التحسن. استمرت هذه الدورة (اختبار -> اكتشاف -> إصلاح -> إعادة اختبار) حتى أصبح النظام قادراً على تحمل 2000 مستخدم متزامن بكل أريحية وبدون أي أخطاء.
نصائح من قلب الميدان (من خبرة أبو عمر)
- لا تنتظر الكارثة: اجعل اختبارات الأداء جزءاً أساسياً من دورة حياة التطوير (CI/CD pipeline)، وليس شيئاً تفعله فقط عند حدوث مشكلة.
- ابدأ صغيراً: لا تحاول محاكاة مليون مستخدم من أول مرة. ابدأ بحمل صغير، وافهم النتائج، ثم زد الحمل تدريجياً.
- راقب كل شيء: اختبار الأداء بدون مراقبة (Monitoring) للخوادم وقواعد البيانات والتطبيق هو نصف العمل. استخدم أدوات مثل Prometheus, Grafana, Datadog لترى الصورة كاملة.
- المشكلة ليست دائماً في الكود: يا جماعة، تذكروا أن عنق الزجاجة (Bottleneck) يمكن أن يكون في الشبكة، أو إعدادات قاعدة البيانات، أو نظام التشغيل، وليس فقط في الكود الذي كتبته.
الخلاصة: نام مرتاح البال 😉
تجربة انهيار الموقع كانت قاسية، لكنها علمتنا درساً لن ننساه. اختبار الإجهاد ليس مجرد أداة تقنية، بل هو تغيير في العقلية. هو الانتقال من مطور “متفاعل” يركض لإصلاح الأعطال، إلى مهندس “استباقي” يبني أنظمة قوية وقادرة على الصمود.
في الإطلاق التالي، كنت أشرب الشاي بالنعناع وأنا أراقب لوحة التحكم بهدوء وثقة. كان الموقع يعمل كالساعة تحت ضغط آلاف المستخدمين. تلك الليلة، نمت مرتاح البال.
نصيحتي الأخيرة لك: من الأفضل أن تكسر موقعك بنفسك في بيئة آمنة ومُتحكّم بها، على أن يكسره لك المستخدمون في أسوأ وقت ممكن.