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

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

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

بعد دقائق من التحليل المحموم، وجدنا الجاني. خدمة صغيرة، “خدمة التوصيات بالمنتجات” التي تستخدم خوارزميات الذكاء الاصطناعي، كانت قد دخلت في حالة فشل وبدأت ترجع أخطاء. المشكلة أن كل صفحة في الموقع تقريبًا (الرئيسية، صفحة المنتج، سلة الشراء) كانت تستدعي هذه الخدمة. ومع فشلها، كانت كل الطلبات تنتظر وتنتظر… حتى تستهلك كل الخيوط (Threads) المتاحة في خوادم الويب، مما أدى إلى شلل النظام بأكمله. فشل صغير في زاوية نائية من النظام تسبب في انهيار كامل. في تلك الليلة المريرة، تعلمنا بالطريقة الصعبة معنى “الفشل المتتالي”، وعرفنا أننا بحاجة ماسة إلى “قاطع دائرة”.

ما هو جحيم الفشل المتتالي (Cascading Failures)؟

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

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

الحل المنقذ: نمط قاطع الدائرة (Circuit Breaker)

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

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

باختصار، قاطع الدائرة هو وكيل ذكي يقف بين الخدمة المستدعِية (Your App) والخدمة المستدعَاة (External Service). هو يراقب صحة الخدمة الخارجية ويتخذ قرارًا بشأن ما إذا كان يجب تمرير الطلب أم رفضه فورًا.

كيف يعمل قاطع الدائرة؟ الحالات الثلاث

يعمل قاطع الدائرة من خلال المرور بثلاث حالات رئيسية:

  1. الحالة المغلقة (Closed): هذا هو الوضع الطبيعي. يكون “القاطع مغلقًا”، والطلبات تتدفق من تطبيقك إلى الخدمة الخارجية دون أي عوائق. في هذه الأثناء، يقوم قاطع الدائرة بمراقبة عدد مرات فشل هذه الطلبات في الخفاء. إذا تجاوز عدد الإخفاقات المتتالية حدًا معينًا (مثلاً، 5 إخفاقات في 30 ثانية)، “يتعثر” القاطع وينتقل إلى الحالة المفتوحة.
  2. الحالة المفتوحة (Open): الآن “القاطع مفتوح”. أي طلب جديد يحاول المرور إلى الخدمة الخارجية يتم رفضه على الفور دون حتى محاولة الاتصال بها. هذا هو الجزء السحري! بدلاً من أن ينتظر تطبيقك 30 ثانية ليفشل، فإنه يحصل على خطأ فوري. هذا يحمي تطبيقك من استنزاف الموارد ويمنح الخدمة الفاشلة مساحة للتنفس والتعافي. يبقى القاطع في هذه الحالة لمدة محددة مسبقًا (مثلاً، 60 ثانية).
  3. الحالة نصف المفتوحة (Half-Open): بعد انقضاء فترة الانتظار في الحالة المفتوحة، ينتقل القاطع إلى هذه الحالة التجريبية. إنه يقول: “حسنًا، سأعطي الخدمة فرصة أخرى”. يسمح القاطع لطلب واحد فقط (أو عدد قليل جدًا من الطلبات) بالمرور إلى الخدمة الخارجية.
    • إذا نجح هذا الطلب التجريبي: يفترض القاطع أن الخدمة قد تعافت، ويعود إلى الحالة المغلقة (Closed)، وتعود الحياة إلى طبيعتها.
    • إذا فشل الطلب التجريبي: يستنتج القاطع أن الخدمة لا تزال تواجه مشاكل، فيعود فورًا إلى الحالة المفتوحة (Open) ويبدأ فترة انتظار جديدة.

لنطبّق الكلام على أرض الواقع: مثال عملي

النظرية رائعة، لكن دعونا نرى كيف يمكن تطبيق هذا عمليًا. أحد أشهر المكتبات لتطبيق أنماط المرونة (Resilience Patterns) في عالم .NET هي مكتبة Polly. سأستخدمها هنا في مثال مكتوب بلغة C#.

تطبيق قاطع الدائرة باستخدام مكتبة Polly في C#

لنفترض أن لدينا طريقة تستدعي خدمة خارجية قد تفشل أحيانًا.


// هذا هو الكود الذي نريد حمايته
// يقوم باستدعاء واجهة برمجة تطبيقات (API) خارجية قد تكون غير مستقرة
private async Task<string> CallUnstableService()
{
    // في الواقع، هذا سيكون استدعاء HttpClient.GetAsync مثلاً
    Console.WriteLine("محاولة استدعاء الخدمة...");
    // سنقوم بمحاكاة فشل الخدمة بشكل عشوائي
    if (Random.Shared.Next(1, 5) > 2) // 60% نسبة فشل
    {
        throw new HttpRequestException("Service is unavailable");
    }
    return "Data from service";
}

الآن، سنقوم بإنشاء “سياسة” قاطع دائرة باستخدام Polly لحماية هذا الاستدعاء.


using Polly;
using Polly.CircuitBreaker;

// تعريف سياسة قاطع الدائرة
// سيتم فتح الدائرة بعد 3 محاولات فاشلة متتالية
// وستبقى مفتوحة لمدة 30 ثانية
AsyncCircuitBreakerPolicy<string> breakerPolicy = Policy
    .Handle<HttpRequestException>()
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 3,
        durationOfBreak: TimeSpan.FromSeconds(30),
        onBreak: (exception, timespan) => 
        {
            Console.WriteLine($"الدائرة فُتحت! سننتظر {timespan.TotalSeconds} ثانية. السبب: {exception.Message}");
        },
        onReset: () => 
        {
            Console.WriteLine("الدائرة أُغلقت! عادت الأمور لطبيعتها.");
        },
        onHalfOpen: () => 
        {
            Console.WriteLine("الدائرة الآن في حالة نصف مفتوحة، سنجرب طلبًا واحدًا.");
        }
    );

// ... في مكان ما في الكود حيث تستدعي الخدمة

while (true)
{
    try
    {
        // تنفيذ الاستدعاء من خلال سياسة قاطع الدائرة
        string result = await breakerPolicy.ExecuteAsync(CallUnstableService);
        Console.WriteLine($"نجاح! النتيجة: {result}");
    }
    catch (BrokenCircuitException)
    {
        // هذا الخطأ يحدث عندما تكون الدائرة مفتوحة، والطلب يتم رفضه فورًا
        Console.WriteLine("الدائرة مفتوحة. لن نحاول الاتصال بالخدمة الآن.");
    }
    catch (HttpRequestException ex)
    {
        // هذا الخطأ يحدث عندما تكون الدائرة مغلقة أو نصف مفتوحة ويفشل الطلب
        Console.WriteLine($"فشل استدعاء الخدمة: {ex.Message}");
    }

    await Task.Delay(2000); // الانتظار ثانيتين بين كل محاولة
}

ما يفعله هذا الكود بسيط وقوي للغاية. بدلاً من استدعاء `CallUnstableService()` مباشرة، نقوم بتمريرها إلى `breakerPolicy.ExecuteAsync()`. تتولى Polly الباقي: تتبع حالات الفشل، وتغيير حالة القاطع، ورمي استثناء `BrokenCircuitException` عندما تكون الدائرة مفتوحة.

نصائح من “الختيار”: كيف تستخدم قاطع الدائرة كالمحترفين

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

1. لا تضع نفس الإعدادات لكل الخدمات

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

2. الأهم: التعامل مع “الحل البديل” (Fallback)

عندما يفتح القاطع، لا يكفي أن تُرجع خطأ للمستخدم. هذا أفضل من لا شيء، لكن الأفضل بكثير هو توفير تجربة بديلة. هذا ما يسمى بالـ Fallback.

  • إذا فشلت خدمة التوصيات، اعرض قائمة بالمنتجات الأكثر مبيعًا بشكل عام.
  • إذا فشلت خدمة البحث، اعرض رسالة لطيفة مع رابط لتصفح الفئات.
  • إذا فشلت خدمة جلب بيانات الملف الشخصي، استخدم نسخة مخبأة (cached) قديمة من البيانات.

في Polly، يمكنك دمج هذا بسهولة:


// تعريف سياسة بديل (Fallback)
AsyncPolicy<string> fallbackPolicy = Policy<string>
    .Handle<BrokenCircuitException>()
    .Or<HttpRequestException>()
    .FallbackAsync(
        fallbackValue: "{"products": ["منتج 1", "منتج 2"]}", // بيانات بديلة من الكاش أو بيانات ثابتة
        onFallback: (result, context) => 
        {
            Console.WriteLine("تم تفعيل الحل البديل (Fallback).");
            return Task.CompletedTask;
        }
    );

// دمج السياستين معًا
AsyncPolicyWrap<string> resiliencePolicy = fallbackPolicy.WrapAsync(breakerPolicy);

// الاستخدام
string result = await resiliencePolicy.ExecuteAsync(CallUnstableService);
Console.WriteLine($"النتيجة النهائية (قد تكون من الخدمة أو من الـ Fallback): {result}");

3. المراقبة والتنبيهات (Monitoring & Alerting)

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

4. اضبط الـ Timeouts بعناية

قاطع الدائرة يعمل بشكل أفضل مع مهلة زمنية (Timeout) قصيرة. لا فائدة من انتظار 60 ثانية لفشل طلب ما. اضبط مهلة زمنية قصيرة (مثلاً 2-5 ثوانٍ). إذا لم تستجب الخدمة خلال هذه الفترة، اعتبرها فاشلة ودع قاطع الدائرة يقوم بعمله.

الخلاصة: من الفوضى إلى المرونة 🧘

في عالم الأنظمة الموزعة المعقد، الفشل ليس احتمالًا، بل هو حتمية. هدفنا كمهندسين ليس بناء أنظمة لا تفشل أبدًا، بل بناء أنظمة تستطيع الصمود والتعافي برشاقة عند حدوث الفشل. نمط قاطع الدائرة هو أحد أهم الأدوات في ترسانتنا لتحقيق هذه “الهندسة المرنة” (Resilience Engineering).

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

أبو عمر

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

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

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

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

آخر المدونات

الحوسبة السحابية

كانت الأحمال المفاجئة تكسر ظهرنا: كيف أنقذتنا الحوسبة بدون خوادم (Serverless) من جحيم التوسع اليدوي؟

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

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

كنت أعرف الإجابة التقنية ولكني أرسب: كيف أنقذتني طريقة ‘STAR’ من جحيم المقابلات السلوكية؟

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

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

من كوابيس التحقق اليدوي إلى ثورة الـ eKYC: قصتي مع إنقاذ الشركات من الاحتيال والتأخير

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

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

اجتماعاتنا كانت حقل ألغام: كيف أنقذتنا ‘السلامة النفسية’ من جحيم الصمت والخوف من الخطأ؟

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

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

كان كل تحديث CSS مغامرة مرعبة: كيف أنقذنا ‘الاختبار البصري التراجعي’ من جحيم ‘لقد بدت أفضل على جهازي’؟

أشارككم قصة حقيقية عن كارثة تسببت بها بضعة أسطر من CSS، وكيف وجدنا الخلاص في تقنية "الاختبار البصري التراجعي" (Visual Regression Testing). مقالة عملية للمطورين...

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