كنا نطلق الميزات على أمل ألا ينهار النظام: كيف أنقذنا اختبار الحِمل (Load Testing) باستخدام k6 من جحيم التخمين؟

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

جاءت ساعة الصفر، ضغطنا زر النشر، وراقبنا الشاشات. في الدقائق الأولى، كان الوضع “لوز” كما نقول في فلسطين. كل شيء يعمل بسلاسة. بدأت رسائل التهنئة تتبادل بين أعضاء الفريق. ولكن، الفرحة لم تدم طويلاً. فجأة، بدأت التنبيهات تنهال علينا كالمطر: “High CPU Usage”, “503 Service Unavailable”, “Database Connection Timeout”. انهار النظام. تحولت ليلة الاحتفال إلى كابوس من إعادة تشغيل الخوادم يدوياً، والبحث اليائس عن سبب العطل في سجلات الأخطاء (logs)، والرد على سيل من رسائل العملاء الغاضبين. في تلك الليلة، لم نكن مبرمجين، بل كنا رجال إطفاء نحاول إخماد حريق أشعلناه بأنفسنا.

هذه الحادثة كانت جرس إنذار قوياً لنا. أدركنا أننا كنا نطلق الميزات ونحن نعتمد على الدعاء والأمل، بدلاً من الاعتماد على البيانات والأرقام. من هنا بدأت رحلتنا مع اختبارات الأداء، وتحديداً مع أداة k6 التي أنقذتنا من جحيم التخمين.

لماذا كنا نعيش في جحيم التخمين؟

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

الاعتماد على الحدس لا الأرقام

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

الاختبارات الوظيفية لا تكفي

كان لدينا تغطية اختبارات ممتازة (unit, integration, E2E tests). كل الاختبارات كانت خضراء قبل النشر. لكن هذه الاختبارات تجيب على سؤال “هل الميزة تعمل؟” (Does it work?)، لكنها لا تجيب أبداً على سؤال “هل ستستمر الميزة بالعمل تحت الضغط؟” (Will it work at scale?).

الخوف من المجهول

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

نقطة التحول: اكتشاف k6

بعد ليلة الكارثة تلك، عقدنا العزم على أن يتغير كل شيء. بدأنا رحلة بحث عن أدوات اختبار الأداء (Performance Testing Tools). جربنا بعض الأدوات الكلاسيكية مثل JMeter، لكنها كانت تبدو معقدة وذات واجهة استخدام قديمة. إلى أن عثرنا على k6.

لماذا k6 بالذات؟

k6 كانت بمثابة نسمة هواء منعشة. لقد صُممت من قبل المطورين، ولأجل المطورين. وهذا ما جعلها خيارنا الأول للأسباب التالية:

  • صديقة للمطورين (Developer-Friendly): تُكتب نصوص الاختبار (scripts) بلغة JavaScript (ES6)، وهي لغة يعرفها ويحبها كل مطور ويب. لا حاجة لتعلم لغة جديدة أو التعامل مع واجهات رسومية معقدة.
  • أداء عالٍ: الأداة نفسها مكتوبة بلغة Go، مما يجعلها قادرة على توليد حِمل كبير من جهاز واحد دون استهلاك موارد ضخمة.
  • مفتوحة المصدر ومجتمعية: لديها مجتمع نشط، وتوثيق ممتاز، والنسخة مفتوحة المصدر قوية جداً وتلبي معظم الاحتياجات.
  • الفحوصات والعتبات (Checks and Thresholds): هذه كانت الميزة القاتلة بالنسبة لنا. تتيح لك k6 تعريف معايير النجاح والفشل مباشرة داخل الكود. على سبيل المثال: “يجب أن يكون معدل الأخطاء أقل من 1%” و “يجب أن يكون 95% من الطلبات أسرع من 500ms”.
  • قابلة للتوسعة والدمج: يمكن دمجها بسهولة تامة مع أنظمة التكامل المستمر والنشر المستمر (CI/CD) مثل Jenkins, GitLab CI, GitHub Actions.

لنبدأ العمل: أول اختبار حِمل لنا مع k6

الكلام النظري جميل، لكن دعونا نرى كيف يمكننا استخدام k6 بشكل عملي. سأريكم مدى سهولة البدء.

تثبيت k6 (أسهل مما تتخيل)

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

brew install k6

كتابة أول سكربت: اختبار بسيط لواجهة برمجية (API)

لنفترض أننا نريد اختبار واجهة برمجية بسيطة. كل ما نحتاجه هو إنشاء ملف باسم script.js وكتابة الكود التالي:

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

// خيارات الاختبار
export const options = {
  // تعريف عدد المستخدمين الافتراضيين (Virtual Users) ومدة الاختبار
  vus: 10, // 10 مستخدمين يرسلون الطلبات بشكل متزامن
  duration: '30s', // يستمر الاختبار لمدة 30 ثانية
};

// الدالة الافتراضية التي سيتم تنفيذها بشكل متكرر من قبل كل مستخدم افتراضي
export default function () {
  // إرسال طلب GET إلى واجهة برمجية تجريبية
  const res = http.get('https://test-api.k6.io/public/crocodiles/1/');

  // التوقف المؤقت لمدة ثانية واحدة لمحاكاة تفكير المستخدم قبل الطلب التالي
  sleep(1);
}

هذا الكود بسيط وواضح. نحن نُعرّف 10 مستخدمين افتراضيين (VUs) سيقومون بإرسال طلبات بشكل متزامن لمدة 30 ثانية. كل مستخدم سيقوم بإرسال طلب GET ثم ينتظر لمدة ثانية.

تشغيل الاختبار وتحليل النتائج

لتشغيل الاختبار، نفتح الطرفية (Terminal) في نفس مجلد الملف ونكتب الأمر:

k6 run script.js

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

...
     ✓ status was 200

     checks.........................: 100.00% ✓ 125       ✗ 0
     data_received..................: 37 kB   1.2 kB/s
     data_sent......................: 11 kB   377 B/s
     http_req_blocked...............: avg=2.83ms   min=5.2µs    med=8.1µs    max=55.51ms  p(90)=12.5µs   p(95)=14.12µs
     http_req_connecting............: avg=912.4µs  min=0s       med=0s       max=17.75ms  p(90)=0s       p(95)=0s
     http_req_duration..............: avg=137.91ms min=130.3ms  med=135.5ms  max=171.1ms  p(90)=143.2ms  p(95)=146.9ms
       { expected_response:true }...: avg=137.91ms min=130.3ms  med=135.5ms  max=171.1ms  p(90)=143.2ms  p(95)=146.9ms
     http_req_failed................: 0.00%   ✓ 0         ✗ 125
     http_req_receiving.............: avg=153.2µs  min=45.2µs   med=137.5µs  max=434.6µs  p(90)=210µs    p(95)=250.3µs
     http_req_sending...............: avg=42.4µs   min=13.1µs   med=38.6µs   max=134.4µs  p(90)=61.6µs   p(95)=74.1µs
     http_req_tls_handshaking.......: avg=1.9ms    min=0s       med=0s       max=37.7ms   p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=137.7ms  min=130.1ms  med=135.3ms  max=170.9ms  p(90)=143ms    p(95)=146.7ms
     http_reqs......................: 125     4.160756/s
     iteration_duration.............: avg=1.13s    min=1.13s    med=1.13s    max=1.17s    p(90)=1.14s    p(95)=1.14s
     iterations.....................: 125     4.160756/s
     vus............................: 10      min=10      max=10
     vus_max........................: 10      min=10      max=10
  • http_req_duration: هذا هو المقياس الأهم. إنه يخبرك كم من الوقت استغرق الطلب من إرساله حتى استلام الرد كاملاً. ستلاحظ وجود avg (المتوسط)، min (الأقل)، max (الأقصى)، والأهم من ذلك p(95).
  • p(95) أو (95th percentile): هذا الرقم يعني أن 95% من طلباتك كانت أسرع من هذه القيمة. إنه مقياس أكثر واقعية من المتوسط، لأنه يتجاهل القيم المتطرفة (الـ 5% الأبطأ) ويعطيك فكرة حقيقية عن تجربة معظم المستخدمين.
  • http_req_failed: نسبة الطلبات التي فشلت. هدفك دائماً أن يكون هذا الرقم 0% أو قريباً جداً منه.
  • http_reqs: العدد الإجمالي للطلبات التي تم إرسالها خلال الاختبار.

من البساطة إلى الاحتراف: سيناريوهات متقدمة

الاختبار السابق كان مجرد بداية. قوة k6 الحقيقية تظهر في السيناريوهات الأكثر تعقيداً التي تحاكي سلوك المستخدم الحقيقي.

الـ Thresholds: خط الدفاع الأول ضد تدهور الأداء

هنا يا جماعة الخير، تبدأ المتعة الحقيقية. الأرقام وحدها لا تكفي. يجب أن نُعلّم الاختبار ما هو “النجاح” وما هو “الفشل”. هذا ما تفعله العتبات (Thresholds). لنقم بتعديل خياراتنا في ملف script.js:

export const options = {
  vus: 10,
  duration: '30s',
  thresholds: {
    // 95% من الطلبات يجب أن تكتمل في أقل من 200 ميللي ثانية
    'http_req_duration': ['p(95)<200'], 
    
    // معدل الأخطاء يجب أن يكون أقل من 1%
    'http_req_failed': ['rate<0.01'], 
    
    // يجب أن يكون عدد الطلبات المنفذة أكثر من 20
    'http_reqs': ['count>20'],
  },
};

الآن، عندما تشغل الاختبار، لن يكتفي k6 بعرض الأرقام، بل سيخبرك بصراحة إذا ما تم تجاوز هذه العتبات أم لا. سيظهر علامة (✓) خضراء بجانب كل شرط تم تحقيقه، وعلامة (✗) حمراء بجانب كل شرط فشل. هذا يجعل عملية التحقق آلية ومثالية لدمجها في الـ CI/CD. إذا فشل اختبار الأداء، يفشل الـ build بأكمله، ويتم منع نشر الكود الذي سبب التدهور.

محاكاة سلوك المستخدم الحقيقي (Scenarios)

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

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

export const options = {
  scenarios: {
    // سيناريو للمستخدمين الذين يتصفحون فقط
    browse_scenario: {
      executor: 'constant-vus',
      vus: 5,
      duration: '1m',
      exec: 'browse', // يربط السيناريو بالدالة 'browse'
    },
    // سيناريو للمستخدمين الذين يقومون بعملية الشراء
    checkout_scenario: {
      executor: 'per-vu-iterations',
      vus: 2,
      iterations: 5,
      exec: 'checkout', // يربط السيناريو بالدالة 'checkout'
      startTime: '30s', // يبدأ هذا السيناريو بعد 30 ثانية
    },
  },
};

export function browse() {
  http.get('https://example.com/products');
  sleep(2);
}

export function checkout() {
  http.get('https://example.com/cart');
  sleep(1);
  http.post('https://example.com/checkout', {});
}

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

نصائح من قلب الميدان (خبرة أبو عمر)

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

  • ابدأ صغيراً ثم توسّع: لا تحاول محاكاة نظامك بأكمله في اليوم الأول. ابدأ بأهم واجهة برمجية (API) لديك، تلك التي تتلقى معظم الطلبات أو التي تسبب معظم المشاكل. اكتب لها اختباراً بسيطاً، ثم أضف التعقيد تدريجياً.
  • لا تختبر على بيئة الإنتاج (Production)! (إلا إذا كنت تعرف تماماً ماذا تفعل): اختبار الحِمل قد يؤدي إلى انهيار الخوادم. دائماً قم بالاختبار على بيئة اختبار (Staging) تكون مطابقة لبيئة الإنتاج قدر الإمكان من حيث المواصفات والبيانات.
  • اختبر باستمرار: لا تجعل اختبار الأداء حدثاً سنوياً. قم بدمجه في الـ CI/CD الخاص بك. قم بتشغيل اختبار حِمل صغير مع كل pull request، واختبار أكبر مع كل عملية نشر على بيئة الاختبار. هذا يساعدك على اكتشاف تدهور الأداء في وقت مبكر جداً.
  • الأرقام وحدها لا تكفي، اربطها بالمراقبة: k6 يخبرك أن “الواجهة X بطيئة”. لكنه لا يخبرك “لماذا”. يجب أن تراقب خوادمك (CPU, Memory, Disk I/O) وقاعدة بياناتك (Slow Queries) أثناء تشغيل الاختبار. أدوات مثل Prometheus, Grafana, Datadog هي صديقك هنا.
  • حدد أهدافك قبل الاختبار: قبل كتابة سطر كود واحد، اسأل نفسك: “ما هو الهدف من هذا الاختبار؟”. هل تريد معرفة نقطة الانهيار (Breaking Point)؟ أم تريد التأكد من أن النظام يستطيع خدمة 1000 مستخدم بزمن استجابة أقل من 300ms؟ تحديد الهدف يحدد شكل الاختبار.

الخلاصة: من الدعاء إلى البيانات 📊

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

اختبار الأداء ليس ترفاً، بل هو جزء أساسي من دورة حياة تطوير أي تطبيق جاد. وأداة مثل k6 جعلت هذا الأمر في متناول جميع المطورين، بغض النظر عن حجم الفريق أو الميزانية.

نصيحتي الأخيرة لك: لا تنتظر الكارثة لتبدأ. ابدأ اليوم، ولو بخطوة صغيرة. قم بتثبيت k6، واكتب اختباراً بسيطاً لأحد مشاريعك. صدقني، راحة البال التي ستشعر بها قبل كل إطلاق ميزة جديدة لا تقدر بثمن. 🙏

أبو عمر

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

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

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

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

آخر المدونات

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

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من جحيم التوقفات المجدولة؟

هل سئمت من إيقاف الخدمة مع كل تحديث لهيكلة قاعدة البيانات؟ أشارككم قصة حقيقية وكيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من ليالي النشر الطويلة والمُجهدة،...

4 يونيو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت إعادة المحاولة كارثة: كيف أنقذتنا مفاتيح عدم تكرار العمليات (Idempotency Keys) من جحيم الفواتير المزدوجة؟

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

4 يونيو، 2026 قراءة المزيد
الحوسبة السحابية

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

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

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

كانت مهمتي البرمجية للاختبار مجرد كود: كيف أنقذني توثيق القرارات من جحيم الصمت بعد المقابلة؟

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

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

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

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

كان كل خادم لدينا ‘ندفة ثلج’ فريدة: كيف أنقذنا ‘الكود كبنية تحتية’ (IaC) من جحيم الانجراف اليدوي؟

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

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

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

كنا نظن أن تغطية الاختبار بنسبة 100% هي درعنا الواقي، لكن الأخطاء كانت تتسلل إلى الإنتاج كاللصوص في ليل بهيم. اكتشف كيف أنقذنا "الاختبار الطفري"...

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