المستوى المطلوب: محترف (Advanced) — يفترض أنك تعرف Redis، Python، ومفاهيم الـ TTL والـ Concurrency الأساسية
الـ Cache بتاعك بيشتغل تمام لمدة ساعة كاملة، وفجأة في ثانية واحدة الـ Database بياخد 4,800 طلب متزامن وبيدخل overload. السبب مش traffic spike ولا هجوم، السبب إن مفتاح Cache واحد popular انتهى TTL بتاعه فكل الـ workers قرروا في نفس اللحظة يبنوه من تاني. المشكلة دي اسمها Cache Stampede أو Thundering Herd، والحل في 30 سطر Redis مع Python.
المشكلة باختصار — مثال السوبر ماركت
قبل ما ندخل في التعريف العلمي، خد المثال ده: تخيّل سوبر ماركت فيه 4 كاشير، وكل كاشير قدّامه طابور 10 ناس. لما كاشير واحد يقفل فجأة، الـ 10 ناس بتوعه مش بيتوزّعوا بهدوء على الكاشير التانية. كلهم بيتدافعوا في نفس اللحظة على الكاشير اللي جنبه. الكاشير ده بيتعطّل، الطابور بتاعه بيكبر، الناس بتزعل، ومن غير ما تحس كل المحل بقى في حالة انهيار.
ده بالظبط اللي بيحصل في الـ Cache. لما key واحد popular ينتهي TTL، كل الـ workers اللي محتاجين القيمة دي بيتدافعوا على الـ DB في نفس الميلي ثانية. الـ DB اللي كان بيخدم 50 طلب في الثانية بقى فجأة بياخد 5000 طلب، وبيقع.
تعريف Cache Stampede علميًا
Cache Stampede — المعروف برضه باسم Cache Miss Storm أو Thundering Herd أو Dogpile — هو نمط فشل بيحصل لما عدد كبير من الطلبات المتزامنة بتعمل cache miss على نفس المفتاح في نفس النافذة الزمنية، فبتروح كلها للمصدر الأصلي (DB، API خارجي، حساب مكلف) في وقت واحد بدل ما واحد فقط يبني القيمة والباقي يقروها من الـ Cache.
النتيجة: الـ DB اللي كان حمله طبيعي (50 RPS مثلاً) بيتعرّض فجأة لحمل أعلى بـ 100x، فبيدخل في حالة overload وبيرفع الـ latency لكل العمليات التانية حتى للـ tenants التانية على نفس السيرفر.
ليه بتحصل المشكلة دي تحديدًا
الافتراض اللي شغّال على نظامك من غير ما تحس: عندك endpoint بيعرض trending products، الحساب مكلف ويستغرق 1.2 ثانية، ومخزّن في Redis بـ TTL = 3600 ثانية. الـ Cache بيشتغل ممتاز، الـ DB بياخد طلب واحد كل ساعة. الكود الساذج كده:
def get_trending():
cached = r.get("trending")
if cached:
return json.loads(cached)
# cache miss
data = expensive_db_query() # 1.2 seconds
r.setex("trending", 3600, json.dumps(data))
return data
المشكلة في الثانية اللي الـ key بينتهي فيها. أي طلب ما بيلاقيش الـ value، فبيفترض إنه مسؤول عن البناء. عندك 5000 RPS = 5000 worker بيدخلوا في نفس اللحظة على نفس السطر expensive_db_query(). الـ DB بياخد 5000 query متطابق في 1.2 ثانية، capacity بتاعه 200 RPS بس، فبيدخل overload.
الـ triggers بتكون عادةً: مفتاح popular جدًا (top 1% traffic)، حساب مكلف (≥ 500ms)، TTL ثابت بدون jitter، عدد كبير من workers خلف load balancer (10+ instances).