كانت بياناتنا في حالة فوضى دائمة: كيف أنقذتنا خوارزمية Raft من جحيم الانقسام العقلي (Split-Brain)؟

يا جماعة الخير، السلام عليكم ورحمة الله.

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

لكن الفرحة لم تدم طويلاً. بعد حوالي نصف ساعة، بدأت تصلنا رسائل غريبة من فريق الدعم. مستخدمون يشتكون من أن بياناتهم تختفي وتظهر! مستخدم يضيف عنصراً إلى سلته، يحدّث الصفحة فيختفي العنصر. مستخدم آخر يرى بيانات قديمة قام بتحديثها قبل دقائق. نظرنا إلى قواعد البيانات الموزعة لدينا، وهنا كانت الصدمة: كل نسخة (replica) من قاعدة البيانات تحكي قصة مختلفة! كأن كل خادم في نظامنا قرر أن “يستقل” ويعيش في عالمه الخاص. أذكر أني صرخت في زملائي مازحاً بيأس: “ولاد عمي، شكلها الخوادم عاملة انقسام زينا!”.

كنا نعيش الكابوس الأسوأ لأي مهندس أنظمة موزعة: حالة “الانقسام العقلي” أو الـ Split-Brain. في تلك الليلة، أدركنا أن بنيتنا التحتية كانت قنبلة موقوتة، وأننا بحاجة ماسة إلى حل جذري يعيد “الإجماع” والنظام إلى عالمنا الرقمي الفوضوي. هذه قصتي مع جحيم الأنظمة الموزعة، وكيف كان الفارس المخلّص هو خوارزمية بسيطة وأنيقة تدعى Raft.

ما هو الجحيم الذي كنا نعيشه؟ (شرح الأنظمة الموزعة ومشكلة الانقسام العقلي)

قبل أن نغوص في تفاصيل الحل، دعونا نفهم أصل المشكلة. معظم التطبيقات الحديثة لا تعمل على خادم واحد، بل على مجموعة من الخوادم التي تعمل معًا، وهذا ما نسميه “النظام الموزع” (Distributed System). والسبب بسيط: نحتاج إلى هذا التوزيع لتحمل الضغط العالي (Scalability) وتجنب توقف الخدمة إذا تعطل أحد الخوادم (Fault Tolerance).

حكايتنا مع الأنظمة الموزعة

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

هذا بالضبط ما يحدث في الأنظمة الموزعة. الخوادم هي “الطهاة”، والبيانات هي “قائمة الطلبات”. يجب أن تتفق جميع الخوادم على “حقيقة” واحدة وموحدة للبيانات.

كابوس “الانقسام العقلي” (Split-Brain)

المشكلة التي واجهناها، الـ Split-Brain، تحدث عندما ينقطع الاتصال بين مجموعتين (أو أكثر) من الخوادم في النظام الموزع. تخيل أن شبكة الاتصال في مطبخنا تعطلت، وأصبح لدينا مجموعتان من الطهاة معزولتان عن بعضهما البعض.

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

في حالتنا، حدث انقطاع بسيط في الشبكة (Network Partition) قسّم عنقود الخوادم (Cluster) إلى مجموعتين غير متساويتين. كل مجموعة انتخبت “قائداً” خاصاً بها وبدأت في قبول عمليات الكتابة على البيانات. والنتيجة؟ فوضى عارمة.

البحث عن طوق النجاة: رحلتنا نحو “الإجماع” (Consensus)

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

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

كنا بحاجة إلى شيء مفهوم، عملي، ويمكننا تطبيقه بثقة. وهنا، ظهر اسم Raft.

الفارس المُخلّص: خوارزمية Raft بالتفصيل المُبسّط

Raft هي خوارزمية إجماع تم تصميمها خصيصاً لتكون أسهل فهماً من Paxos. هدفها الأساسي هو تحقيق نفس درجة الأمان والموثوقية، ولكن ببنية أوضح. تعتمد Raft على تقسيم مشكلة الإجماع إلى ثلاث مشاكل فرعية وأكثر قابلية للإدارة:

  1. انتخاب القائد (Leader Election)
  2. تكرار السجلات (Log Replication)
  3. الأمان (Safety)

دعونا نشرحها بأسلوب بسيط، مستخدمين تشبيه “الانتخابات في قرية صغيرة” المكونة من عدة “عُقد” (Nodes).

الأدوار الثلاثة: القائد، المرشح، والتابع (Leader, Candidate, Follower)

في أي لحظة، كل خادم (عقدة) في نظام Raft يكون في إحدى الحالات الثلاث:

  • القائد (Leader): هو “مختار” القرية. يوجد قائد واحد فقط في كل مرة. هو الوحيد الذي يتلقى الطلبات من العملاء (المستخدمين) ويدير عملية تحديث البيانات.
  • التابع (Follower): هو “أهالي” القرية. دوره سلبي، فهو فقط يستجيب لطلبات القائد والمرشحين. كل الخوادم تبدأ كـ “توابع”.
  • المرشح (Candidate): هو “أحد الأهالي” الذي قرر ترشيح نفسه ليصبح “المختار” الجديد عندما يغيب المختار الحالي (القائد).

انتخاب القائد (Leader Election)

هنا تبدأ الروعة. كيف تتجنب Raft وجود أكثر من قائد؟ عبر عملية انتخاب ديمقراطية ومنظمة:

  1. كل “تابع” لديه مؤقت عشوائي (Election Timeout). طالما أنه يتلقى رسائل “نبضات قلب” (Heartbeats) من القائد الحالي، فإنه يعيد ضبط المؤقت ويبقى تابعاً سعيداً.
  2. إذا انقضى وقت المؤقت دون أن يسمع التابع من القائد (ربما لأن القائد تعطل أو أصبح معزولاً)، يفترض التابع أن القائد “مات”.
  3. عندها، يغير التابع حالته إلى “مرشح”. يزيد من رقم “الفترة الانتخابية” (Term)، يصوّت لنفسه، ثم يرسل طلبات تصويت إلى جميع الخوادم الأخرى.
  4. الخوادم الأخرى تصوّت للمرشح الأول الذي يطلب منها ذلك في تلك الفترة الانتخابية.
  5. إذا حصل المرشح على أصوات الأغلبية (Quorum)، فإنه يفوز في الانتخابات ويصبح القائد الجديد. ويبدأ فوراً بإرسال نبضات القلب إلى الجميع ليعلن عن قيادته.
  6. إذا حدث انقسام في الأصوات ولم يفز أحد (أكثر من مرشح في نفس الوقت)، تنتهي الفترة الانتخابية، وتبدأ فترة جديدة مع مؤقتات عشوائية جديدة، مما يقلل من احتمالية حدوث انقسام آخر في التصويت.

تكرار السجلات (Log Replication)

بمجرد انتخاب قائد، يصبح هو المسؤول الوحيد عن تغيير البيانات. تتم هذه العملية كالتالي:

  1. العميل يرسل طلباً (مثلاً: “حدّث القيمة X إلى 10”) إلى القائد.
  2. القائد يضيف هذا الطلب كـ “إدخال” (Entry) جديد في سجله (Log) الخاص. هذا الإدخال يكون في حالة “غير مُلزمة” (Uncommitted).
  3. يقوم القائد بإرسال هذا الإدخال الجديد إلى جميع التوابع عبر رسائل (AppendEntries).
  4. عندما يستلم التابع الإدخال، يضيفه إلى سجله الخاص ويرسل تأكيداً إلى القائد.
  5. عندما يتلقى القائد تأكيدات من أغلبية الخوادم، يعتبر الإدخال “مُلزماً” (Committed). هذا هو مفتاح الأمان: التغيير لن يضيع حتى لو تعطل القائد، لأن الأغلبية تملكه الآن.
  6. يقوم القائد بتطبيق التغيير على “آلة الحالة” (State Machine) الخاصة به (أي تحديث البيانات الفعلية) ويرسل رداً ناجحاً إلى العميل.
  7. في الرسائل التالية، يخبر القائد التوابع بأن الإدخال أصبح مُلزماً، فتقوم هي الأخرى بتطبيقه على آلات الحالة الخاصة بها.

كيف أنقذتنا Raft من الانقسام العقلي؟

الآن، لنعد إلى مشكلة الـ Split-Brain. تخيل أن لدينا 5 خوادم، وانقطعت الشبكة وقسمتهم إلى مجموعة من 3 (المجموعة أ) ومجموعة من 2 (المجموعة ب).

  • المجموعة (أ) – الأغلبية: هذه المجموعة تحتوي على أغلبية العقد (3 من 5). لذلك، يمكنها انتخاب قائد جديد من بينها. هذا القائد الجديد يمكنه تلقي الطلبات وتأكيدها (لأنه يستطيع الحصول على تأكيد من الأغلبية، أي 3 خوادم). النظام يستمر في العمل بشكل طبيعي في هذا الجزء.
  • المجموعة (ب) – الأقلية: هذه المجموعة لديها عقدتان فقط. عندما يحاول أحد الخوادم هنا أن يصبح قائداً، لن يتمكن أبداً من الحصول على أصوات الأغلبية (يحتاج إلى 3 أصوات على الأقل، ولكنه لا يستطيع التواصل إلا مع خادم واحد آخر). بالتالي، ستظل هذه المجموعة بدون قائد، ولن تقبل أي طلبات كتابة جديدة.

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

نصائح من “الختايرة”: كيف تطبق Raft بنجاح؟

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

لا تخترع العجلة من جديد!

أهم نصيحة يمكن أن أقدمها: لا تحاول كتابة تطبيق Raft الخاص بك من الصفر للأنظمة الإنتاجية إلا إذا كنت باحثاً في هذا المجال. الخوارزمية تبدو بسيطة، لكن تفاصيلها الدقيقة وحالاتها النادرة (Edge Cases) تجعل تطبيقها الصحيح أمراً بالغ الصعوبة. بدلاً من ذلك، استخدم مكتبات موثوقة ومُختبرة في المعارك الحقيقية.

أمثلة على مكتبات ممتازة:

  • Go: مكتبة etcd/raft هي المعيار الذهبي. يستخدمها نظام etcd الشهير، وهي قوية ومستقرة.
  • Java: هناك مشاريع مثل Copycat أو Apache Ratis.
  • Rust: هناك مكتبة raft-rs، وهي منفذ لمكتبة etcd.

مثال عملي (بسيط) باستخدام Go

لإعطائكم فكرة عن الشكل الذي يبدو عليه استخدام مكتبة Raft، هذا مثال مفاهيمي مبسط جدًا باستخدام مكتبة `etcd/raft`. هذا ليس كودًا كاملاً، بل مجرد توضيح للفكرة:


// هذا الكود هو مجرد مثال توضيحي للفكرة، وليس تطبيقاً كاملاً

package main

import (
	"context"
	"log"
	"go.etcd.io/etcd/raft/v3"
	"go.etcd.io/etcd/raft/v3/raftpb"
)

// كل عقدة في نظامنا سيكون لها هذا الهيكل
type RaftNode struct {
	id          uint64          // معرّف فريد للعقدة (1, 2, 3...)
	node        raft.Node       // كائن Raft الفعلي من المكتبة
	storage     *raft.MemoryStorage // مكان تخزين السجلات (Log)
	// ... المزيد من الحقول لإدارة الشبكة وآلة الحالة
}

func NewRaftNode(id uint64, peers []raft.Peer) *RaftNode {
	storage := raft.NewMemoryStorage()
	cfg := &raft.Config{
		ID:              id,
		ElectionTick:    10, // 10 "ticks" لانتهاء مهلة الانتخاب
		HeartbeatTick:   1,  // "tick" واحد لإرسال نبضة قلب
		Storage:         storage,
		MaxSizePerMsg:   1024 * 1024,
		MaxInflightMsgs: 256,
	}

	// إنشاء عقدة Raft
	node := raft.StartNode(cfg, peers)

	return &RaftNode{
		id:      id,
		node:    node,
		storage: storage,
	}
}

func (rn *RaftNode) Propose(data []byte) {
	// اقتراح تغيير جديد على النظام
	// المكتبة ستتولى عملية تكرار هذا التغيير وإجماع الأغلبية عليه
	rn.node.Propose(context.TODO(), data)
}

// في الحلقة الرئيسية للبرنامج، ستقوم بما يلي:
func (rn *RaftNode) run() {
	// ...
	// for {
	//     select {
	//     case <-ticker.C:
	//         rn.node.Tick() // دفع عجلة الزمن في Raft
	//     case rd := <-rn.node.Ready():
	//         // هنا تتعامل مع كل ما هو جاهز من Raft
	//         // 1. حفظ الحالات الجديدة (HardState, Entries) في تخزين دائم
	//         rn.storage.Append(rd.Entries)
	//         // 2. إرسال الرسائل (rd.Messages) إلى العقد الأخرى عبر الشبكة
	//         // ... send(rd.Messages)
	//         // 3. تطبيق الإدخالات المُلزمة (rd.CommittedEntries) على آلة الحالة الخاصة بك
	//         // ... apply(rd.CommittedEntries)
	//         // 4. إخبار المكتبة بأنك انتهيت من معالجة هذه الدفعة
	//         rn.node.Advance()
	//     }
	// }
}

كما ترون، المكتبة تتولى منطق Raft المعقد، ومهمتك هي “توصيل الأسلاك”: توفير التخزين، إرسال الرسائل عبر الشبكة، وتطبيق التغييرات المُلزمة.

المراقبة والضبط (Tuning)

تطبيق Raft ليس نهاية المطاف. يجب أن تراقب أداءه باستمرار. بعض المؤشرات المهمة:

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

الخلاصة: من الفوضى إلى النظام 🤝

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

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

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

وفقكم الله في رحلتكم البرمجية.

أبو عمر

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

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

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

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

آخر المدونات

تجربة المستخدم والابداع البصري

كان المستخدمون يقاتلون واجهاتنا: كيف أنقذتنا ‘قوانين تجربة المستخدم’ من جحيم التصميم غير البديهي؟

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

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

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

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

2 يونيو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذتنا استراتيجية التخزين المؤقت الجانبي (Cache-Aside) من جحيم الاستعلامات؟

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

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

كابوس التحقق اليدوي من الهوية: كيف أنقذنا الـ eKYC من جحيم الاحتيال وتجربة المستخدم السيئة

أشارككم قصة من قلب المعاناة في شركتنا الناشئة، وكيف انتقلنا من التحقق اليدوي الكارثي من هويات العملاء إلى نظام آلي (eKYC) قائم على الذكاء الاصطناعي....

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

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

أشارككم قصة حقيقية من قلب المعركة التقنية، عن ليلة كاد فيها "الانجراف التكويني" أن يدمر مشروعنا. اكتشفوا كيف كانت "البنية التحتية كشيفرة" (IaC) طوق النجاة...

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