Partial Failures
على كمبيوتر واحد: يا الحاجة شغالة يا مش شغالة (deterministic). لو فيه عطل في الهاردوير، الكمبيوتر غالبًا بيقع كله (أحسن من إنه يطلع نتيجة غلط). في السيستم الموزع: جزء بايظ وجزء شغال، وبشكل nondeterministic — ممكن مش عارف حتى إذا كانت العملية نجحت!
لو نود باظت، وقّف كله وارجع من checkpoint. كأنه كمبيوتر واحد كبير.
لازم تفضل شغالة دايمًا، هاردوير عادي (أعطال أكتر)، شبكة IP، آلاف النودز = دايمًا فيه حاجة بايظة. لازم fault tolerance في السوفتوير.
الشبكة مش موثوقة
معظم الأنظمة الموزعة asynchronous packet networks: مفيش ضمان إمتى الرسالة توصل ولا إذا كانت هتوصل. لما تبعت request ومايجيلكش رد، ممكن يكون:
مستحيل تعرف الفرق. الحل الوحيد العملي: timeout. وفي الواقع: كابلات بتتقطع (حتى أسماك قرش بتعض كابلات تحت البحر!)، أخطاء configuration، swich firmware upgrade بيوقف الشبكة دقايق، network partitions. حتى لو نادرة لازم السوفتوير يعرف يتعامل معاها.
اكتشاف الأعطال
أحيانًا فيه feedback صريح (connection refused لو مفيش process على البورت، الـ OS بيبلّغ لو الـ process مات، أو الـ switch يقولك اللينك وقع). بس غالبًا لازم timeout + retries، وفي الآخر تعلن إن النود ماتت.
Timeouts والتأخير المفتوح (Unbounded delay)
- Timeout طويل: انتظار طويل قبل ما تكتشف الوفاة.
- Timeout قصير: ممكن تعلن نود ماتت وهي بس بطيئة → شغلها يتنقل لغيرها → ضغط أكتر → cascading failure. وممكن العملية تتنفذ مرتين.
- ليه التأخير متغير؟ الطوابير في كل حتة: الـ switch، الـ OS لما الـ CPUs مشغولة، الـ VM monitor، TCP flow control، وإعادة إرسال TCP. وفي الكلاود: noisy neighbors.
- الحل الأذكى: timeout تكيّفي بيقيس توزيع الـ response time (زي Phi Accrual failure detector في Akka و Cassandra).
الساعات مش موثوقة
كل ماكينة ليها ساعة (كريستال كوارتز) مش دقيقة 100%. ومحتاجين الوقت في: timeouts، percentiles، صلاحية الكاش، timestamps للأحداث.
وقت الحائط (System.currentTimeMillis()). متزامن بـ NTP — يعني ممكن يرجع لورا فجأة! وبيتجاهل leap seconds غالبًا. ماينفعش لقياس المدد.
(System.nanoTime()) بيمشي لقدام دايمًا. ينفع لقياس المدد (timeouts). قيمته المطلقة مالهاش معنى، ومينفعش تقارنه بين ماكينتين.
الاعتماد على الساعات — أمثلة خطر
- LWW بالـ timestamps (Cassandra, Riak): نود ساعتها متأخرة كتاباتها هتضيع في صمت حتى لو حصلت بعد! والـ timestamps مش بتفرق بين sequential و concurrent. الحل: logical clocks (عدادات متزايدة) بدل الساعات الفيزيائية.
- الساعة رقم بـ confidence interval مش نقطة. Google Spanner بيستخدم TrueTime API اللي بيرجع [earliest, latest]، ولو فترتين مش متقاطعتين يبقى الترتيب أكيد. وبيستنى طول الفترة قبل الـ commit (وعشان كده بيحطوا GPS وساعات ذرية في كل داتاسنتر عشان الفترة ~7ms).
- Snapshot isolation موزع محتاج transaction IDs متزايدة على مستوى global — الساعات ممكن تنفع لو الـ uncertainty معروف (Spanner).
Process Pauses ⏸️
أسباب التوقف: GC stop-the-world (أحيانًا دقايق)، VM اتعملها suspend/live migration، اللابتوب اتقفل، context switch و steal time، disk I/O متزامن (حتى class loading في Java)، swapping/thrashing، أو SIGSTOP. وعلى ماكينة واحدة عندنا mutexes و semaphores، بس الأنظمة الموزعة مفيهاش shared memory.
حلول جزئية: أنظمة hard real-time (طيارات وعربيات — مكلفة جدًا وبتقلل throughput)، أو التعامل مع الـ GC كـ outage مخطط (النود تتشال من الترافيك قبل الـ GC، أو تعمل restart دوري).
الحقيقة والأكاذيب: مين بيحدد الحقيقة؟
النود مش بتقدر تثق في حكمها على نفسها. ممكن تبقى شغالة بس الكل شايفها ميتة (رسايلها مش واصلة، أو كانت واقفة GC).
Fencing Tokens 🎫
مشكلة: client أخد lock، عمل GC pause طويل، الـ lock انتهى وأخده client تاني، الأول صحي وكتب وهو فاكر نفسه لسه ماسك → داتا بايظة (حصلت فعلًا في HBase).
الـ server نفسه هو اللي بيتأكد — مش الكلاينت. (ZooKeeper: zxid أو cversion ينفعوا كـ fencing token.)
Byzantine Faults 😈
نود بتكدب (بقصد أو بسبب عطل) — بتبعت ردود متناقضة أو مزيفة. Byzantine fault-tolerant = السيستم يشتغل صح حتى لو فيه نودز خاينة (أقل من الثلث). مهم في الطيران (radiation بيبوظ الذاكرة) والـ blockchain/الأنظمة بين أطراف مش واثقة في بعض. لكن في داتاسنتر بتاعك، غالبًا بنفترض إن مفيش Byzantine faults — والحماية البسيطة كفاية: checksums، validation للمدخلات، أكتر من سيرفر NTP.
System Models — افتراضاتنا الرسمية
| التوقيت | أعطال النودز |
|---|---|
| Synchronous: delay محدود (مش واقعي). · Partially synchronous: غالبًا محدود بس أحيانًا لأ ← الأكثر واقعية. · Asynchronous: مفيش أي افتراض توقيت، ولا حتى ساعة. | Crash-stop: بتقع وماترجعش. · Crash-recovery: بتقع وترجع، والـ stable storage بيفضل ← الأكثر واقعية. · Byzantine: أي حاجة. |
🧪 Lab
- في Node: قارن
Date.now()وperformance.now()— غيّر ساعة الجهاز وانت شغال وشوف مين اتأثر. - اعمل HTTP call لسيرفر بيعمل
setTimeoutعشوائي من 0 لـ 5 ثواني، وجرّب timeouts مختلفة: كام request اعتبرته “مات” وهو كان هيرد؟ - شغّل Node بـ
--trace-gcعلى script بيعمل allocations كتير وشوف الـ pauses.