STACK://UP الأقاليم 0/9RTL · AR / قفز · ? اختصارات UP

الإقليم ٤ — مشكلة الحالة: النسخ المتماثل (Primary-Replica)

النبذة

في الإقليم ٣ وزّعنا المستخدمين على خادمين بلا عناء — لأن طبقتَي web و app عديمتا الحالة (stateless): أي طلبٍ يصلح على أي خادم، فلا فرق. لكن لكل خادمٍ قاعدة بيانات، والبيانات حالة (state). وهنا تنكسر السهولة. هذا الإقليم يجيب عن سؤال Task 1: «كيف يعمل عنقود Primary-Replica، وما الفرق بين العقدة الأساسية والنسخة من منظور التطبيق؟»

اللغز المستفزّ

موزّعك (round robin) أرسل مستخدماً ليغيّر بريده إلى خادم A، فكتب التغيير في قاعدة بيانات A. بعد ثانيةٍ أعاد التحميل، فأرسله الموزّع إلى خادم B، فقرأ من قاعدة بيانات B… البريد القديم. الموقع «نسي» ما كتبه للتوّ.

لماذا نجح توزيع الـ web/app وفشل توزيع البيانات؟ وكيف تجعل الخادمين يريان نفس البيانات دون أن تخلق تعارضاً؟ صمّم حلّك على ورقة. ستكتشف أنك أمام مقايضةٍ قاسية: نسخةٌ واحدةٌ من الحقيقة (SPOF) مقابل نسختين قد تتعارضان.

الدرس

ليش الحالة تختلف عن الكود — جذر المشكلة

الكود يُقرأ فقط وقت التشغيل: نسختان متطابقتان منه تعطيان نفس النتيجة دائماً، فاستنساخه مجاني. البيانات تُكتب وتتغيّر: اللحظة التي يكتب فيها خادمٌ بياناً، تصبح نسخته مختلفةً عن البقية. نسختان من قاعدة بيانات تقبلان الكتابة بحرّية = حقيقتان متناقضتان (write conflict). لذلك لا يمكن «مجرّد نسخ» قاعدة البيانات كما نسخنا الـ app. نحتاج بروتوكولاً يقرّر من يملك الحقيقة، وكيف تنتشر النسخ منها.

ليش Primary-Replica — تصميمٌ يحلّ التعارض بقرارٍ واحد

أبسط حلٍّ للتعارض: اسمح بالكتابة في مكانٍ واحدٍ فقط. هذا هو جوهر Primary-Replica (الاسم القديم Master-Slave):

كيف تبقى الـ replica محدَّثة؟ الأساسية تسجّل كل تغييرٍ في سجلّ (في MySQL اسمه binary log / binlog)، والنسخة تقرأ هذا السجلّ وتعيد تنفيذ التغييرات على نفسها. غالباً غير متزامن (asynchronous): الأساسية لا تنتظر النسخة، فقد تتأخّر النسخة لحظاتٍ خلف الأساسية (replication lag) — وهذا بالضبط ما سبّب لغز «البريد القديم». لكنه ثمنٌ مقبولٌ مقابل عدم التعارض.

ليش هذا يفيد التطبيق — التقسيم Read/Write

الآن صار للتطبيق قاعدتان بدورين مختلفين، وهذا ما يسأل عنه Task 1 («الفرق من منظور التطبيق»):

إذاً الفرق من منظور التطبيق: الأساسية = حيث أكتب (وأقرأ ما يجب أن يكون فورياً)؛ النسخة = حيث أقرأ لأخفّف الحِمل. النسخة لا تقبل كتابةً من التطبيق إطلاقاً.

لاحظ

فائدة جانبية للتوفّر: إن ماتت الأساسية، يمكن «ترقية» نسخةٍ لتصير الأساسية الجديدة (failover) — وهذا تطبيقٌ عمليٌّ لـ Active-Passive من الإقليم ٣ على طبقة البيانات.

الوجه الآخر للعملة: لماذا «كاتبٌ واحد» مشكلة (سؤال Task 2)

Task 2 يسألك عن عيب هذه البنية: «لماذا وجود خادم MySQL واحدٍ قادرٍ على قبول الكتابة مشكلة؟». طبّق العدسة:

(الحلول الأعمق — multi-primary، sharding — خارج نطاق هذا المشروع عمداً؛ المشروع يقول صراحةً «لا تدخل تفاصيل لم تُطلب». اعرف أن المشكلة موجودة وسببها، لا أكثر.)

بذرة Task 2/3: لماذا «كل الخوادم بنفس المكوّنات» مشكلة

لاحظ بنية Task 1: كل خادمٍ يحوي web + app + db معاً. Task 2 يسألك لماذا قد يكون هذا مشكلة. الجواب يولد من فصل الاهتمامات (إقليم ١):

هذا بالضبط ما يدفع Task 3 إلى فصل المكوّنات على خوادمها الخاصّة (إقليم ٧). سجّله.

تحليل الأخطاء

التمرين (سبورة)

أضِف إلى مخطّط Task 1 أسهم النسخ المتماثل: سهمٌ من الأساسية إلى كل نسخة (تدفّق binlog)، وسمِّ أيُّ قاعدةٍ primary وأيُّها replica. ثم على ورقة:

  • اشرح في ٣ أسطر كيف يعمل Primary-Replica (writes→primary، binlog→replica، reads→replica).
  • اكتب الفرق من منظور التطبيق (أين أكتب، أين أقرأ).
  • اكتب عيبَي «الكاتب الواحد» (SPOF كتابة + اختناق كتابة)، وعيبَ «كل المكوّنات على كل خادم».

الخلاصة — وصلٌ في الشجرة

البذرة المتروكة: حتى الآن كل همّنا التوفّر والتوزيع. لكن الموقع الآن مكشوفٌ للإنترنت بلا حماية، وحركته تمرّ عاريةً يقرؤها أي متنصّت. قبل أن نوسّع أكثر، يجب أن نمنع الدخيل ونختم الكلام. هذا هو الإقليم ٥.


على السبورة (إنجليزي، مكثّف)

A Primary-Replica (Master-Slave) MySQL cluster has one Primary that accepts writes, and one or more Replicas that are read-only copies. The Primary records every change in its binlog; each Replica reads that log and replays it to stay in sync (usually asynchronously, so replicas can lag slightly).

From the application's view: it sends all writes to the Primary and spreads reads across Replicas. The Replica never takes writes from the app.

Why one writable MySQL is a problem: the Primary is a write SPOF and a write bottleneck — you can scale reads but not writes.

Why identical servers (db+web+app each) is a problem: components contend for the same resources and can't be scaled independently.

وقفة الانضباط: لا تذكر sharding/Galera/group-replication ما لم يُسأل. المشروع يطلب primary-replica فقط.