تجارب الفوضى: طبقة التطبيق
تجارب الفوضى: طبقة التطبيق
تختبر تجارب الفوضى على مستوى البنية التحتية (إيقاف العُقد، استنزاف الحاويات) مرونة المنصة. أما تجارب الفوضى على مستوى التطبيق فتختبر ما إذا كانت شيفرة الخدمة قادرة على التعامل مع الواقع الفوضوي للأنظمة الموزعة: التبعيات البطيئة، والإخفاقات الجزئية، واستجابات الخطأ غير المتوقعة. هذه هي الأعطال التي تُوقظك فعلاً في الثالثة صباحاً — ليس خادماً متوقفاً تماماً، بل بوابة دفع تُعيد استجابة 503 في 60% من الطلبات بينما تنجح الـ40% الأخرى.
يتناول هذا الدرس ثلاث فئات من الأعطال على مستوى التطبيق: حقن زمن الاستجابة، وإخفاقات التبعية، وحقن الأخطاء. تستهدف كل فئة نمط إخفاق مختلف، وتتطلب منهجاً تجريبياً مختلفاً.
1. حقن زمن الاستجابة
التأخير هو القاتل الصامت في الأنظمة الموزعة. الخدمة المُجاورة التي تستغرق 3 ثوانٍ بدلاً من 30 ميلي ثانية لا تُعيد خطأً — تنتظرها شيفرتك بسعادة، محتجزةً goroutine واتصالاً بقاعدة البيانات وفتحة في thread pool. تحت الحمل العالي، يتفاقم هذا ليصبح تراكماً في قوائم الانتظار ثم انهياراً متسلسلاً. هذا هو نمط "الإخفاق الرمادي": كل شيء يبدو مُشتغلاً، لكن النظام ينزف.
الهدف من حقن زمن الاستجابة هو التحقق من أن كل طلب شبكي في خدمتك يمتلك مهلة زمنية، وأن هذه المهلة أقصر من SLO الخاص بك، وأن التبعية البطيئة لا تستنزف مواردك.
حقن التأخير مع Toxiproxy: Toxiproxy وكيل TCP قابل للبرمجة مُصمم خصيصاً للفوضى. تضعه بين خدمتك وتبعياتها، ثم تتحكم في الأعطال عبر HTTP API دون لمس أي من الخدمتين.
في Kubernetes مع Istio: يمكن لـVirtualService الذي يحتوي على مواصفة fault.delay حقن التأخير عبر طبقة الشبكة دون لمس أي شيفرة تطبيق.
طبّق هذا أثناء اختبار الحمل وراقب: هل تحترم خدمتك مهلتها الخاصة؟ هل تتخلى عن الطلبات بأناقة، أم تقوم بتكديسها حتى ينفد الذاكرة؟
2. إخفاقات التبعية
لكل خدمة تبعيات: قواعد بيانات، ذاكرات تخزين مؤقت، طوابير رسائل، واجهات برمجية خارجية. تُجيب تجارب إخفاق التبعية على السؤال: "ماذا تُعيد خدمتنا حين لا تتوفر X تماماً؟"
الجواب المتوقع للتبعيات غير الحرجة: تتدهور الخدمة بأناقة، مُعيدةً استجابة جزئية أو بيانات مُخزنة مؤقتاً، مع التسجيل والتنبيه. الجواب المتوقع للتبعية الحرجة: تُعيد الخدمة خطأً واضحاً بسرعة (fast-fail)، لا بعد سلسلة مهلات تمتد 30 ثانية.
محاكاة انقطاع التبعية مع Istio (إلغاء HTTP):
شغّل هذا على مسار قراءة غير حرج وتحقق أن خدمتك تُعيد استجابة مُخزنة مؤقتاً أو متدهورة. ثم شغّله على مسار كتابة حرج وتأكد أن الخطأ يظهر للمُستدعي بوضوح بدلاً من الفقدان الصامت للبيانات.
3. حقن الأخطاء
يتجاوز حقن الأخطاء مفهوم "الخدمة متوقفة" ليختبر كيف تتعامل شيفرتك مع استجابات سيئة محددة: 500 Internal Server Error، و429 Too Many Requests، وJSON مشوهاً، وإخفاقات المصادقة، والاستجابات المبتورة. تتضمن حوادث الإنتاج الحقيقية غالباً تبعية تقنياً تعمل لكنها تستجيب بشكل خاطئ — شهادة TLS منتهية الصلاحية، أو ترحيل مخطط كسر صيغة الاستجابة، أو حد معدل مُفعّل بسبب جار ضوضاء.
بالنسبة لخدمات HTTP، يغطي fault.abort في Istio رموز الحالة. لحالات أكثر دقة — ترويسات content-type خاطئة، حمولات جزئية، استجابات ضخمة — استخدم وكيل أعطال مُخصص أو امتداد service mesh.
Chaos Monkey لـSpring Boot (SDK داخل التطبيق): للخدمات التي تمتلك شيفرتها، تحقن SDKs داخل العملية أعطالاً على مستوى الدالة، دون أي وكيل شبكة. هذا مفيد لاختبار منطق إعادة المحاولة في كود الـclient الخاص بك وفي البيئات التي لا تستطيع فيها تشغيل sidecar.
ربط التجارب بالمراقبة
تجربة الفوضى على مستوى التطبيق لا تساوي شيئاً بدون مراقبة جيدة. قبل حقن أي عطل، تأكد من توفر:
- حالة ثابتة مُعرَّفة: زمن استجابة p99 < 200ms، معدل خطأ < 0.1%، عمق الطابور < 50.
- لوحات مراقبة مفتوحة: مقاييس RED الخاصة بخدمتك (Rate, Errors, Duration) وصحة التبعية.
- مفتاح إيقاف جاهز: أمر واحد أو نقرة واحدة تُزيل العطل فوراً.
- تنبيهات مُعدَّلة بشكل مناسب: أو لا — تشغيل التجارب مع التنبيهات الحقيقية هو اختبار صالح لدليل تشغيل المناوبة.
بعد كل تجربة، وثّق ما انكسر، وما صمد، وما يحتاج إصلاحاً. النتيجة ليست الفشل — النتيجة هي الفجوة بين مرونتك المفترضة والواقع المقيس.