اعتقدت أن تطبيقي صاروخ… حتى سحقه 100 مستخدم: كيف كشف اختبار الحِمل عنق الزجاجة الخفي؟

يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.

اسمحوا لي أبدأ معكم بقصة صارت معي قبل كم سنة. كنت وقتها شغال على تطبيق شخصي سميته “جدولي”، فكرته بسيطة: أداة لإدارة المهام والمواعيد للطلاب والمهنيين. قضيت شهور وأنا أبرمج فيه، “أكتك” كل تفصيلة، وأهتم بواجهة المستخدم، وأتأكد إنه كل شيء شغال “زي الحلاوة”. على جهازي، وعلى سيرفر التطوير الصغير اللي كنت حاطه، كان التطبيق “صاروخ” بكل معنى الكلمة. أي عملية بتصير بأقل من 100 ميللي ثانية. كنت فخور جدًا بالنتيجة ومتحمس للإطلاق.

قررت أعمل إطلاق بسيط ومحدود، نشرت التطبيق في مجموعة لطلاب جامعتي القديمة على فيسبوك. في أول ساعة، كان كل شيء تمام. 10 مستخدمين، 20، 30… والأمور مستقرة. فجأة، بدأت توصلني رسايل: “أبو عمر، التطبيق بطيء جدًا!”، “يا رجل الصفحة الرئيسية مش راضية تفتح!”، “كل ما أضيف مهمة بعلّق!”.

فتحت لوحة المراقبة (Monitoring Dashboard) وإذ بي أرى الكارثة: استهلاك المعالج (CPU) 100%، والذاكرة (RAM) على وشك الامتلاء، وزمن استجابة الطلبات (Response Time) بالثواني وليس بالميللي ثانية. كان عدد المستخدمين النشطين بالكاد تجاوز الـ 100 مستخدم. شعرت بإحباط شديد، كيف لتطبيق سريع جدًا أن ينهار بهذا الشكل؟ هنا كانت الصدمة الأولى، والإدراك بأن “تطبيقي سريع على جهازي” هي واحدة من أكبر الأكاذيب التي نكذبها على أنفسنا كمبرمجين. هذه الحادثة كانت درسي القاسي والمفيد الذي أدخلني بعمق إلى عالم اختبارات الأداء، وتحديدًا “اختبار الحِمل” (Load Testing).

ما هو اختبار الحِمل (Load Testing) وليش هو مهم؟

ببساطة يا جماعة، اختبار الحِمل مش عشان يشوف إذا الكود تبعك شغال أو فيه أخطاء منطقية (Bugs). هداك شغل الـ Unit Tests والـ Integration Tests. اختبار الحِمل بيجاوب على سؤال مختلف تمامًا: “كيف سيتصرف تطبيقي تحت ضغط حقيقي ومتوقع من المستخدمين؟”.

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

“اختبار الحِمل لا يختبر “ماذا” يفعله نظامك، بل يختبر “كيف” يفعله تحت الضغط.”

الهدف هو محاكاة عدد معين من المستخدمين الافتراضيين (Virtual Users) الذين يستخدمون تطبيقك في نفس الوقت، ومراقبة أداء النظام لتحديد أقصى قدرة له قبل أن يبدأ الأداء في التدهور أو الانهيار.

الفرق بين اختبار الحِمل وأنواع اختبارات الأداء الأخرى

  • Load Testing (اختبار الحِمل): يقيس الأداء تحت حِمل “متوقع”. مثلاً، تتوقع 1000 مستخدم متزامن في أوقات الذروة، فتختبر النظام بهذا العدد.
  • Stress Testing (اختبار الإجهاد): يدفع النظام إلى ما بعد طاقته الاستيعابية لتحديد نقطة الانهيار. يعني بدل 1000 مستخدم، بتجرب 2000 أو 3000 عشان تشوف متى وكيف بيفشل النظام.
  • Spike Testing (اختبار الارتفاع المفاجئ): يحاكي زيادة مفاجئة وكبيرة في عدد المستخدمين، مثل وقت إعلان تسويقي كبير، ثم عودة العدد إلى طبيعته.

في قصتي، كنت بحاجة ماسة إلى اختبار الحِمل لأفهم سلوك تطبيقي تحت ضغط 100 مستخدم فقط!

رحلة الكشف عن “عنق الزجاجة” (The Bottleneck)

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

  1. Apache JMeter: أداة قوية جدًا، مفتوحة المصدر، مبنية بالجافا. فيها واجهة رسومية (GUI) بتسهل بناء خطط الاختبار المعقدة. لكنها ممكن تكون ثقيلة على الموارد شوي.
  2. k6 (by Grafana Labs): أداة أحدث، مكتوبة بلغة Go، وتستخدم JavaScript/TypeScript لكتابة سكربتات الاختبار. خفيفة جدًا على الموارد، وموجهة للمطورين (Developer-friendly) لأنها بتعتمد على الكود.

بحكم خلفيتي كمطور وحبي للكود، اخترت أبدأ مع k6 لسهولته وسرعته.

الخطوة الأولى: كتابة سكربت اختبار بسيط بـ k6

الهدف كان محاكاة مستخدمين يزورون الصفحة الرئيسية لتطبيق “جدولي”. السكربت كان بسيطًا جدًا:


import http from 'k6/http';
import { sleep } from 'k6';

// هذا هو إعداد الاختبار
export const options = {
  // المراحل: زيادة تدريجية للمستخدمين
  stages: [
    { duration: '30s', target: 50 },   // زيادة إلى 50 مستخدم خلال 30 ثانية
    { duration: '1m', target: 100 },  // زيادة إلى 100 مستخدم خلال دقيقة
    { duration: '30s', target: 0 },    // العودة إلى 0 مستخدم
  ],
  // شروط النجاح: إذا فشل أكثر من 1% من الطلبات، أو كان p95 أعلى من 500ms، اعتبر الاختبار فاشلاً
  thresholds: {
    'http_req_failed': ['rate<0.01'], // معدل الفشل أقل من 1%
    'http_req_duration': ['p(95)<500'], // 95% من الطلبات يجب أن تكون أسرع من 500ms
  },
};

// هذا هو الكود الذي سينفذه كل مستخدم افتراضي
export default function () {
  // طلب GET للصفحة الرئيسية
  http.get('https://api.jidwali.app/tasks');

  // انتظار عشوائي بين 1 و 3 ثواني لمحاكاة سلوك المستخدم
  sleep(Math.random() * 2 + 1); 
}

شرح بسيط للكود:

  • options: هنا نحدد سيناريو الاختبار. في حالتي، طلبت من k6 أن يزيد عدد المستخدمين الافتراضيين (VUs) تدريجيًا إلى 100 مستخدم.
  • thresholds: هذه أهم جزئية. هنا أضع “شروط النجاح”. قلت له إن الاختبار يعتبر فاشلاً إذا كان معدل الطلبات الفاشلة أكثر من 1%، أو إذا كان زمن استجابة 95% من الطلبات (p95) أبطأ من 500 ميللي ثانية.
  • export default function (): هذا هو قلب السكربت. كل مستخدم افتراضي سيقوم بتنفيذ هذا الكود بشكل متكرر: يطلب الصفحة الرئيسية وينتظر قليلاً.

الخطوة الثانية: تشغيل الاختبار ورؤية الحقيقة المرة

شغلت الاختبار، وفي ثوانٍ بدأت الأرقام تظهر على الشاشة. كانت النتائج صادمة ومطابقة تمامًا لما حدث في الواقع:

  • عندما كان عدد المستخدمين 50، كان متوسط زمن الاستجابة حوالي 400ms (مقبول لكن على الحافة).
  • بمجرد أن بدأ العدد يقترب من 100، قفز زمن الاستجابة إلى 3500ms (3.5 ثوانٍ!).
  • معدل الطلبات الفاشلة (http_req_failed) بدأ يرتفع.
  • شرط النجاح p(95)<500 فشل فشلاً ذريعًا.

الآن، أصبح لدي دليل رقمي على المشكلة. لم يعد الأمر مجرد “شعور” بأن التطبيق بطيء، بل أرقام وبيانات تثبت وجود عنق زجاجة حقيقي.

الخطوة الثالثة: تحديد مكان “عنق الزجاجة”

مع وجود البيانات، بدأت رحلة البحث. عنق الزجاجة في تطبيقات الويب غالبًا ما يكون في أحد هذه الأماكن:

  1. قاعدة البيانات (Database): استعلامات بطيئة،缺少 فهارس (indexes)، اتصالات كثيرة.
  2. الكود البرمجي للتطبيق (Application Code): خوارزميات غير فعالة، استدعاءات متكررة للـ API، مشكلة N+1.
  3. البنية التحتية (Infrastructure): سيرفر ضعيف (CPU/RAM)، إعدادات خاطئة لخادم الويب (Nginx/Apache).

بما أن استهلاك المعالج كان 100%، شككت فورًا في قاعدة البيانات. فتحت سجلات الاستعلامات البطيئة (Slow Query Log) في قاعدة بياناتي (كانت PostgreSQL وقتها)، وهنا كانت المفاجأة الكبرى.

وجدت أن استعلامًا واحدًا، مسؤول عن جلب المهام للمستخدم، كان يتكرر مع كل طلب للصفحة الرئيسية، وكان يستغرق وقتًا طويلاً جدًا كلما زاد عدد الصفوف في جدول المهام. المشكلة كانت أنه يقوم بعملية “Full Table Scan”، أي أنه يمر على كل السجلات في الجدول ليبحث عن مهام المستخدم الحالي.

السبب؟ ببساطة، كنت ناسي أضيف فهرس (Index) على عمود user_id في جدول tasks!

الحل كان بسيطًا بشكل يبعث على الضحك (والبكاء بنفس الوقت):


-- إضافة فهرس لعمود `user_id` في جدول `tasks`
CREATE INDEX idx_tasks_user_id ON tasks(user_id);

هذا السطر الواحد يخبر قاعدة البيانات أن تنشئ هيكل بيانات سريع للبحث عن طريق user_id، بدلاً من مسح الجدول بأكمله في كل مرة.

الخطوة الرابعة: إعادة الاختبار والشعور بالنصر 🚀

بعد إضافة الفهرس، قمت بإعادة تشغيل نفس سكربت k6 بالضبط. النتيجة؟ كانت كالليل والنهار.

  • عند 100 مستخدم متزامن، كان متوسط زمن الاستجابة أقل من 80ms.
  • معدل الطلبات الفاشلة: 0%.
  • استهلاك المعالج على السيرفر بالكاد تجاوز 20%.
  • جميع شروط النجاح (Thresholds) تم تحقيقها بنجاح باهر.

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

نصائح أبو عمر الذهبية في اختبارات الأداء

من خلال هذه التجربة وغيرها، تعلمت دروسًا غالية، وأحب أشارككم بعضها:

  1. 💡 ابدأ مبكرًا وبسيطًا: لا تنتظر حتى قبل الإطلاق بأسبوع لتبدأ اختبار الأداء. اكتب سكربت بسيط مثل الذي عرضته واجعله جزءًا من روتينك.
  2. ⚠️ لا تختبر على بيئة الإنتاج (Production) مباشرةً: هذا خطأ قاتل. قم بإنشاء بيئة مطابقة للإنتاج قدر الإمكان (Staging Environment) وقم بإجراء اختباراتك عليها.
  3. 📊 اعرف مقاييسك (Know Your Metrics): ما هو زمن الاستجابة المقبول لتطبيقك؟ 200ms؟ 500ms؟ حدد أهدافك (SLOs – Service Level Objectives) واكتبها في thresholds الخاصة بك.
  4. 🤖 الأتمتة هي المفتاح (Automation is Key): أفضل شيء هو دمج اختبارات الحِمل في مسار الـ CI/CD الخاص بك. مثلاً، يمكنك تشغيل اختبار حِمل صغير كل ليلة على بيئة الـ Staging وإرسال تقرير تلقائي.
  5. 🧠 الأداة مجرد وسيلة: تعلم k6 أو JMeter ممتاز، لكن الأهم هو فهم كيفية تحليل النتائج وربطها بالمشاكل الحقيقية في نظامك. ركز على فهم المقاييس مثل p95, p99, ومعدلات الفشل.

الخلاصة: من مبرمج قلق إلى مهندس واثق ✅

تجربة “جدولي” علمتني أن بناء تطبيق “شغّال” هو نصف الطريق فقط. النصف الآخر، والأكثر أهمية لنجاح أي منتج، هو بناء تطبيق “موثوق” (Reliable) وقادر على الصمود. اختبار الحِمل لم يكن مجرد خطوة تقنية، بل كان نقلة نوعية في طريقة تفكيري، حوّلني من مبرمج يثق بـ “إنها تعمل على جهازي” إلى مهندس يفكر في الضغط، والسعة، وتجربة المستخدم النهائي تحت أي ظرف.

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

أتمنى لكم كل التوفيق في مشاريعكم، ولا تنسوا تختبروا أحمالكم!

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

برمجة وقواعد بيانات

استعلاماتنا كانت تزحف كالسلحفاة: كيف أنقذتنا ‘فهارس قاعدة البيانات الذكية’ من جحيم بطء الأداء؟

قصة حقيقية من قلب المعركة البرمجية، كيف تحولنا من الشكوى من بطء "كارثي" في تطبيقاتنا إلى أداء فائق السرعة. السر لم يكن في زيادة الموارد،...

21 أبريل، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

مساهماتي في المصادر المفتوحة كانت حلمًا مؤجلًا: كيف أنقذتني ‘قضايا المبتدئين الجيدة’ (Good First Issues) من جحيم ‘من أين أبدأ؟’

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

21 أبريل، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

فشل خدمة واحدة كان يُسقط نظامنا بأكمله: كيف أنقذنا ‘نمط قاطع الدائرة’ من جحيم الفشل المتتالي؟

أشارككم قصة حقيقية عن انهيار كاد أن يدمر سمعتنا، وكيف كان نمط تصميم بسيط مثل "قاطع الدائرة" (Circuit Breaker) هو طوق النجاة. سنتعلم معاً كيف...

21 أبريل، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

عمليات الاحتيال كانت أشباحًا في نظامنا: كيف أنقذنا ‘التعلم الآلي لكشف الشذوذ’ من جحيم الخسائر الصامتة؟

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

21 أبريل، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

خوادمنا كانت كائنات ثلجية فريدة: كيف أنقذنا ‘Ansible’ من جحيم ‘الانحراف في الإعدادات’ (Configuration Drift)؟

أنا أبو عمر، وهذه قصتي مع "الانحراف في الإعدادات" (Configuration Drift)، الكابوس الصامت الذي يحوّل خوادمك المنظمة إلى فوضى من "الكائنات الثلجية" الفريدة. اكتشف كيف...

20 أبريل، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

مساراتنا المهنية كانت طريقًا مسدودًا: كيف أنقذتنا ‘مصفوفات الكفاءة’ من جحيم الركود الوظيفي؟

أشارككم قصة حقيقية من تجربتي كقائد فريق، كيف تحول شعور الإحباط والركود لدى المبرمجين إلى حماس وتطور ملموس. اكتشفوا معنا أداة "مصفوفة الكفاءة" البسيطة التي...

20 أبريل، 2026 قراءة المزيد
اختبارات الاداء والجودة

تغطية الكود 100% كانت وهمًا: كيف أنقذنا ‘الاختبار الطفري’ (Mutation Testing) من جحيم الاختبارات عديمة الفائدة؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، يوم ظننا أننا وصلنا للكمال بتغطية اختبار 100%، لنكتشف أننا كنا نطارد وهمًا. اكتشفوا معنا كيف غيّر "الاختبار...

20 أبريل، 2026 قراءة المزيد
البودكاست