اختبار الأداء والتحميل

تصميم اختبارات واقعية

18 دقيقة الدرس 5 من 28

تصميم اختبارات واقعية

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

لماذا تكذب الاختبارات الصناعية؟

يفشل اختبار الحِمل الصناعي البحت بطرق يمكن التنبؤ بها. تُخزِّن مُحسِّنات استعلامات قاعدة البيانات خطط التنفيذ لأشكال الاستعلامات التي تراها خلال الإحماء؛ فاستخدام معرف صف واحد في كل مرة يعني أن المُحسِّن يُعيد خطة مُحسَّنة ومخزنة لا يحصل عليها الإنتاج حين تكون معرفات المستخدمين موزعة بالتساوي على 400 مليون صف. ذاكرة CDN والخوادم الوسيطة تُسخَّن بعناوين URL متطابقة ومتكررة؛ فمعدل الإصابة في اختبارك 95%، لكنه في الإنتاج 40%. تجمعات الاتصالات تُحدد حجمها وفق التزامن الملاحظ؛ فالإحماء ذو الخيط الواحد يُفوِّت مشكلة N+1 للاستعلامات التي لا تظهر إلا حين تضرب 500 coroutine الـ ORM في وقت واحد. هذه ليست حالات حافة — بل هي أسباب شائعة لموافقة الفرق على نظام ينهار في يوم الإطلاق.

بيانات تشبه بيانات الإنتاج

المعيار الذهبي هو لقطة حديثة ومُجهَّلة من بيانات الإنتاج محمَّلة في قاعدة بيانات الاختبار. إخفاء هوية البيانات الشخصية أمر غير قابل للتفاوض: استبدل الأسماء الحقيقية والبريد الإلكتروني بمكافئات مُولَّدة بـ faker، واقتطع رموز الدفع، وقم بتجزئة معرفات المستخدمين بملح قابل للعكس إذا كانت إمكانية التتبع ضرورية للتصحيح. أدوات مثل faker وmimesis وإضافة pg_anonymizer لـ PostgreSQL تتولى هذه المهمة على نطاق واسع.

بالنسبة لمعظم الفرق، يصعب عمليًا استنساخ قاعدة البيانات الإنتاجية بالكامل بسبب حجمها. النهج الصحيح هو أخذ عينات تمثيلية: استنساخ الشكل الإحصائي لبيانات الإنتاج لا كل صف منها. يعني هذا الحفاظ على الكاردينالية (عدد القيم المتميزة)، وانحراف التوزيع (نسبة صغيرة من المستخدمين المتميزين تُولِّد 80% من الكتابات — توزيع زيبف)، والنزاهة المرجعية (المفاتيح الخارجية وجداول الربط). عينة بحجم 10 غيغابايت تعكس توزيع قاعدة بيانات 1 تيرابايت بدقة أكثر فائدة بكثير من عينة 10 غيغابايت تُبالغ في تمثيل الحسابات الجديدة المنشأة في الـ 24 ساعة الأخيرة.

مفهوم أساسي: أخطر تشويه في البيانات هو الذاكرة المؤقتة الباردة عند بدء الاختبار. في الإنتاج، ذاكرة Redis المؤقتة وشبكة CDN لديك دافئتان. إجراء اختبار حِمل على ذاكرة مؤقتة باردة يقيس أداء خادم الأصل تحت أقصى حِمل ضربات خاطئة — سيناريو لا يحدث في الإنتاج إلا بعد مسح كامل للذاكرة المؤقتة أو نشر يُبطل جميع المفاتيح. قرِّر عن قصد: إذا كنت تختبر السلوك بذاكرة باردة (سيناريو صالح)، تقبَّل الأرقام المتضخمة؛ أما إذا كنت تختبر الإنتاجية في الحالة المستقرة، فسخِّن الذاكرة المؤقتة قبل بدء نافذة القياس.

لتوليد بيانات اختبار ذات معاملات، ابنِ ملف CSV أو JSON ووجِّه اختبارك في k6 (أو JMeter) منه بدلًا من تضمين القيم في الكود:

// سكريبت k6: توجيه الطلبات من ملف CSV يحتوي معرفات مستخدمين ومنتجات حقيقية import http from 'k6/http'; import { SharedArray } from 'k6/data'; import { check, sleep } from 'k6'; // SharedArray يُحمَّل مرة واحدة ويُشارَك بين جميع VUs — يتجنب نسخ 100 ألف صف لكل VU const users = new SharedArray('users', function () { return JSON.parse(open('./fixtures/users.json')); }); const products = new SharedArray('products', function () { return JSON.parse(open('./fixtures/products.json')); }); export default function () { const user = users[Math.floor(Math.random() * users.length)]; const product = products[Math.floor(Math.random() * products.length)]; const res = http.get( `https://staging.example.com/api/v1/products/${product.id}`, { headers: { Authorization: `Bearer ${user.token}` } } ); check(res, { 'status 200': (r) => r.status === 200, 'latency < 200ms': (r) => r.timings.duration < 200, }); sleep(Math.random() * 2 + 0.5); // وقت التفكير: 0.5 – 2.5 ثانية }

نمذجة حركة المرور

حركة المرور الحقيقية ليست خطًا مستقيمًا. تتسم بتذبذب يومي وأسبوعي، وارتفاعات حادة تُولِّدها حملات تسويقية أو أحداث فيروسية، وارتفاع تدريجي عند بداية يوم العمل، والقطيع الرعبي thundering herd الذي يحدث حين يُطلق جدول مهام آلاف الإشعارات المؤجلة دفعة واحدة. يجب أن تُنمذج صورة اختبارك هذه الأنماط، لا أحمال الذروة فحسب.

يتبع نموذج حركة المرور النموذجي لخدمة B2C على شبكة الويب منحنى على مدار 24 ساعة يشبه موجة جيبية مع ذروة ثانوية في المساء. استخرج هذا من أداة APM الخاصة بك عبر الاستعلام عن بيانات معدل الطلبات للـ 30 يومًا الماضية وحساب التوزيع الساعي عند P95. يجب أن يُعيد سيناريو الاختبار إنتاج نسخة مضغوطة من هذا المنحنى: ارتفاع إلى ذروة الصباح، ثبات، انخفاض، عودة إلى ذروة المساء، انخفاض تدريجي. المدة الإجمالية للاختبار: 30–60 دقيقة لإعادة إنتاج كاملة للشكل.

يُنفِّذ ramping-vus في k6 هذا مباشرة:

// منفِّذ k6: ramping-vus على شكل منحنى حركة مرور حقيقي export const options = { scenarios: { traffic_shape: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '5m', target: 200 }, // ارتفاع تدريجي صباحي { duration: '10m', target: 500 }, // ذروة الصباح { duration: '5m', target: 300 }, // انخفاض منتصف النهار { duration: '10m', target: 600 }, // ارتفاع حاد للمساء (حدث ترويجي) { duration: '5m', target: 400 }, // تراجع بعد الارتفاع { duration: '10m', target: 700 }, // ذروة المساء { duration: '5m', target: 0 }, // انخفاض تدريجي ], gracefulRampDown: '30s', }, }, thresholds: { http_req_duration: ['p(95)<300', 'p(99)<800'], http_req_failed: ['rate<0.005'], // أقل من 0.5% معدل خطأ }, };

بالإضافة إلى عدد VU، نمذج مزيج الطلبات بدقة. في خدمة تجارة إلكترونية نموذجية، تفوق القراءات الكتابات 9:1. تُشكِّل تصفح الكتالوج والبحث 70% من القراءات؛ وإتمام الطلب وحالته بقية الطلبات. إذا كان اختبارك 100% قراءات كتالوج فأنت لم تختبر التنازع على الكتابة في قاعدة البيانات الذي يُسبِّب ارتفاع تأخير p99 خلال التخفيضات الفورية.

أمانة بيئة الاختبار

بيئة الاختبار التي تُحرِّف طبولوجيا الإنتاج تُنتج نتائج إما مطمئنة كذبًا أو مثيرة للقلق كذبًا — وكلا النتيجتين تُضيِّع وقت الفريق الهندسي. الحد الأدنى لاختبار حِمل ذي معنى هو:

  • نفس فئة الأجهزة. سيُظهر خادم staging بـ 4 vCPUs التشبع عند 3,000 RPS بينما تتحمل حافلة الإنتاج بـ 64 vCPUs أصلية 80,000 RPS. حجِّم staging بـ 1/N من أسطول الإنتاج واضرب النتائج في N — أو الأفضل، اختبر على نسخة إنتاجية تعكس نوع النمط الفعلي وإعداد الحجم التلقائي.
  • نفس طبولوجيا الشبكة. إذا كانت حركة مرور الإنتاج تمر عبر موازن حِمل وشبكة CDN وبوابة API، فيجب أن يسلك الاختبار نفس المسار. الاختبار المباشر على خادم التطبيق يتجاوز كل طبقة تُضيف تأخيرًا أو قد تُصبح عنق زجاجة تحت الحِمل.
  • نفس إعداد الاعتماديات. قلِّد الخدمات الخارجية أو ظلِّلها فقط حين تكون خارج نطاق سيطرتك فعلًا. للخدمات التي تمتلكها، شغِّل نسخًا حقيقية بحجم متناسب. تقليد خدمة المخزون لأن staging لا تملكها يعني أنك لن تكتشف قط أنها تُصبح عنق زجاجة تسلسلي حين يتجاوز إنتاجية الطلبات 200 TPS.
  • العزل عن حركة مرور الإنتاج. لا تُجرِ اختبار حِمل على بيئة الإنتاج إلا مع ضوابط واضحة لنطاق التأثير: feature flags تُوجِّه حركة مرور الاختبار إلى تجمع shadow، أو بيئة canary موصولة بخلفيات حقيقية لكنها تستقبل طلبات صناعية فقط. اختبار حِمل يُسرِّب طلبات صناعية إلى قاعدة بيانات الإنتاج حادثة جودة بيانات.
Realistic Test Environment Topology k6 Load Generator CDN / LB API Gateway App Service x2 replicas Redis Cache pre-warmed DB Replica anon prod snapshot fixtures/ users.json SharedArray معزول عن الإنتاج — حركة الاختبار لا تكتب طلبات حقيقية
طبولوجيا اختبار الحِمل الواقعية: مولِّد مدفوع بالبيانات الثابتة، مسار CDN/LB/بوابة كامل، ذاكرة مؤقتة مُسخَّنة مسبقًا، ولقطة قاعدة بيانات مُجهَّلة — معزولة عن مسار الكتابة في الإنتاج.

وقت التفكير ونمذجة الجلسات

المستخدمون الحقيقيون يتوقفون بين الإجراءات. إنسان يتصفح كتالوج منتجات يقضي 2–15 ثانية يقرأ صفحة قبل النقر على الرابط التالي. حذف وقت التفكير من اختبارك يضغط جلسة المستخدم في حلقة ضيقة تُولِّد 10 أضعاف الطلبات التي يُولِّدها المستخدم الحقيقي، مما يعني أن اختبارك بـ 500 VU يُحاكي فعليًا 5,000 مستخدم نشط متزامن — رقم لم تقصده ولا تستطيع تفسيره. نمذج وقت التفكير كمتغير عشوائي مستخرج من توزيع يطابق تحليلات جلساتك (التوزيع اللوغاريتمي الطبيعي شائع؛ تحقق من قياسات المتصفح).

بالإضافة إلى وقت التفكير، نمذج رحلة المستخدم: التسلسل المرتب للصفحات التي تزورها الجلسة النموذجية. جلسة التجارة الإلكترونية ليست اختيارًا عشوائيًا للـ URL — بل هي: الرئيسية → البحث → صفحة المنتج → سلة التسوق → إتمام الطلب، مع احتمالات تسرب في كل خطوة. معدل تحويل 15% يعني أن 85% من الجلسات لا تصل إلى نقطة نهاية إتمام الطلب. إذا كان اختبارك يضرب نقطة إتمام الطلب بنفس معدل البحث عن المنتجات، فأنت تختبر نمط سلوك غير موجود في الإنتاج.

ممارسة إنتاجية: استخرج أهم 10 رحلات للمستخدم من أدوات إعادة تشغيل الجلسات (FullStory، Amplitude، Mixpanel) أو من سجلات الوصول عبر awk وsort -rn. قم بترميز كل رحلة كمجموعة سيناريو في k6، وعيِّن أوزان VU مطابقة لتوزيع الرحلات الحقيقي، وستحصل على اختبار يمكن لفريق تحليلات الإنتاج التحقق من منطقيته — مكسب كبير في المصداقية عند عرض النتائج للقيادة.
فخ إنتاجي: تُحدِّد كثير من الفرق حجم اختبارها بعدد VU لا بـ RPS المستهدف. عدد VU مقياس معتم: 500 VU بدون وقت تفكير يُولِّد RPS أعلى بكثير مما تُولِّده 500 VU مع 2 ثانية وقت تفكير. اذكر دائمًا أهداف اختبارك بـ RPS أو وصولات/ثانية، وقِس المعدل الفعلي أثناء الاختبار، وتحقق من تطابقه مع حركة مرور الإنتاج التي قصدت محاكاتها. استخدم منفِّذ constant-arrival-rate في k6 حين تحتاج إلى الحفاظ على هدف RPS دقيق بغض النظر عن وقت الاستجابة.

تحديد الحجم الأساسي: مثال عملي

للوصول إلى عدد VU واقعي عند الذروة: من أداة APM الخاصة بك، اقرأ أقصى RPS تحتاج لاستيعابه (لنقل 2,000 RPS). من تحليلات الجلسات، متوسط مدة الجلسة 8 دقائق وكل جلسة تُصدر 40 طلبًا — أي كل مستخدم يُولِّد 40/480 ≈ 0.083 RPS. قانون Little يعطيك: المستخدمون المتزامنون = الإنتاجية / معدل الفرد = 2,000 / 0.083 ≈ 24,000 جلسة متزامنة. هذا هو هدفك من VU عند الذروة، وليس 500. الفرق التي تبدأ بـ "لنجرب 100 VU" لا تُجري اختبار حِمل — بل تُسخِّن JVM.

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