الفوضى ليست خطأ، بل ميزة: التعرف على هندسة الفوضى في C++
\nتخيل: خدمة C++ الخاصة بك تعمل لأشهر دون أعطال. ثم فجأة! - فشل القرص، فقدان حزمة، أو SIGKILL مفاجئ. وينتهي كل شيء. النظام ينهار، المستخدمون غاضبون، وأنت تبحث بشكل محموم عن الخطأ في السجلات. هل هذا الموقف مألوف؟
لقد اعتدنا على اختبار البرامج في ظروف مثالية: شبكة مستقرة، قرص سريع، ذاكرة غير محدودة. لكن الواقع قاسٍ. هنا يأتي دور هندسة الفوضى - النظام الذي يعلمنا كسر النظام عمدًا لجعله أقوى. واليوم سنتحدث عن كيفية تطبيق هذا النهج على تطبيقات C++.
\nهندسة الفوضى لـ C++ ليست مجرد "تشغيل chaos monkey والصلاة". إنها تتعلق بتجارب محكومة تكشف نقاط الضعف قبل أن يجدها المستخدمون. سنتعلم كيفية إدخال أعطال الذاكرة، ومحاكاة الإدخال/الإخراج البطيء، وقطع اتصالات الشبكة - وكل ذلك باستخدام كود C++ نقي.
\n\nلماذا تحتاج C++ بشكل خاص إلى هندسة الفوضى؟
\nلغة C++ تمنح المطور قوة هائلة: إدارة يدوية للذاكرة، وصول منخفض المستوى للموارد، وعدم وجود جامع قمامة. لكن هذه القوة سلاح ذو حدين. خطأ في delete[]، تسرب في غلاف RAII، أو reinterpret_cast غير صحيح يمكن أن يحول نظامًا مستقرًا إلى بيت من ورق هش.
نادرًا ما يختبر اختبار الوحدات التقليدي كيف سيتصرف الكود في حالة:
\n- \n
- نقص الذاكرة العشوائية (
std::bad_alloc) \n - إغلاق مفاجئ لوصف الملف \n
- تأخيرات في الشبكة (latency spikes) \n
- فساد البيانات في الذاكرة المشتركة \n
هندسة الفوضى لـ C++ تسد هذه الفجوات. نحن لا نكتب اختبارات فقط - بل نستفز النظام لارتكاب الأخطاء ونرى إن كان سينجو.
\n\nالأدوات: كيفية إدخال الفوضى في كود C++
\nلإجراء التجارب، سنحتاج إلى أدوات حقن الأخطاء. في C++، يمكننا العمل على مستويات مختلفة:
\n- \n
- على مستوى الكود: اعتراض استدعاءات النظام عبر LD_PRELOAD أو
dlsym\n - على مستوى المكتبات: استبدال المخصصات والمقابس القياسية \n
- على مستوى نظام التشغيل: استخدام
ptraceأو eBPF للتدخل في عمل العملية \n
سنتبع المسار الأكثر عملية - سنكتب مكتبتنا الخاصة لحقن الأخطاء، والتي يمكن دمجها بسهولة في أي مشروع C++.
\n\nمثال 1: محاكاة فشل تخصيص الذاكرة
\nواحدة من أكثر المشاكل شيوعًا في C++ هي std::bad_alloc غير المتوقع. كيف سيتصرف كودك إذا أعاد new استثناءً؟ دعنا نتحقق.
// chaos_memory.h\n#pragma once\n#include <new>\n#include <cstdlib>\n#include <atomic>\n#include <cstddef>\n\nclass ChaosAllocator {\npublic:\n // تعيين احتمالية الفشل (0.0 - 1.0)\n static void set_failure_probability(double prob) {\n failure_probability_ = prob;\n }\n\n static void* allocate(std::size_t size) {\n if (should_fail()) {\n throw std::bad_alloc();\n }\n return std::malloc(size);\n }\n\n static void deallocate(void* ptr) {\n std::free(ptr);\n }\n\nprivate:\n static bool should_fail() {\n // مولد أرقام عشوائية بسيط للعرض\n static std::atomic<unsigned int> seed = 42;\n seed = seed * 1103515245 + 12345;\n return (seed / 65536) % 100 < failure_probability_ * 100;\n }\n\n inline static double failure_probability_ = 0.0;\n};\nالآن يمكننا استبدال المخصص القياسي والتحقق من كيفية معالجة الكود الخاص بنا لأعطال الذاكرة:
\n// main.cpp\n#include <iostream>\n#include <vector>\n#include "chaos_memory.h"\n\n// مخصص مخصص