مستوى المقال: متوسط — هذا الدرس يفترض إنك تعرف Node.js و Express وعملت قبل كده endpoint بسيط، وعندك Redis instance شغّال محلياً أو على cloud (Upstash أو Redis Cloud free tier يكفي). لو لسه مستخدمتش Redis قبل كده، اقرأ أول 4 صفحات من توثيق Redis Quick Start قبل ما تكمل.
لو الـ API بتاعك فجأة بياخد 18,000 طلب/دقيقة من نفس الـ IP وبعد ربع ساعة المستخدمين الشرعيين بيشتكوا من 429 Too Many Requests، المشكلة مش في عدد الطلبات الكبير. المشكلة في إن الـ Rate Limiter بتاعك بيستخدم Fixed Window، والـ Window ده بيتعامل بظلم مع المستخدم اللي عمل burst سريع. Token Bucket في 70 سطر Node.js مع Lua script على Redis بيحل المشكلتين: بيوقف الإساءة الحقيقية، وبيسمح للـ burst الطبيعي يعدّي بدون اعتراض.
المشكلة باختصار
الفريق عندك ضايف express-rate-limit بـ 100 طلب/دقيقة لكل IP. شغّال كده 6 أشهر بدون مشاكل ظاهرة. فجأة بدأ يحصل سيناريو متكرر:
- مستخدم mobile بيفتح التطبيق، التطبيق بيعمل 12 طلب متتالي علشان يحمّل الـ feed.
- بعد دقيقة، نفس المستخدم بيرفّش الصفحة 3 مرات، يبقى دفع 36 طلب في 60 ثانية.
- الـ Window بيتعمله reset في الثانية 0:30 وبيكون فاضي بقي 64 طلب.
- الحساب الموازي بتاع نفس المستخدم (تطبيق ويب على نفس الـ IP) بيستهلك الـ 64 المتبقية في 8 ثوان.
- المستخدم بيشوف 429 في حين إنه طبيعي تماماً.
الـ Fixed Window بيعد الطلبات في فترة ثابتة (مثلاً كل دقيقة) ويعمل reset في نهايتها. ده بيخلّق ظاهرة اسمها boundary spike: لو حد عمل 100 طلب في الثانية 0:59 وبعدها 100 طلب تاني في الثانية 1:01، السيرفر شاف 200 طلب في ثانيتين بدون ما الـ limiter يلاحظ، لأن كل واحد منهم في window مختلف.
ليه Token Bucket هو الحل الأذكى للـ APIs الحديثة
Token Bucket فكرة بسيطة جداً، وأحسن طريقة تفهمها هي بمثال من غير كود قبل ما ندخل في التعريف العلمي.
تخيّل صنبور مياه ودلو فاضي. الصنبور بيقطّر مياه بمعدل ثابت (مثلاً قطرة كل ثانية). الدلو سعته 10 لتر. لما تيجي تستخدم المياه، انت بتستهلك من المخزون اللي في الدلو. لو الدلو فاضي، مفيش مياه دلوقتي وانت لازم تستنّى. لو الدلو امتلى، الصنبور بيقفل أوتوماتيك ومش بيفيض على الأرض.
كل request في الـ API بيـ "يشرب" token واحد من الدلو. الـ refill rate الثابت (مثلاً 2 token/ثانية) بيـ replenish الدلو ببطء. لو المستخدم استخدم الـ API ببطء، الدلو بيمتلئ تدريجياً ولما يحتاج burst عنده 10 tokens جاهزة فوراً. لو استخدم بسرعة، بيستنفد الدلو ويستنّى refill طبيعي.
الفايدة الكبيرة: الـ burst مسموح بيه طول ما الـ average rate في الحدود. ده بيناسب الـ APIs الحديثة لأن المستخدم العادي بيعمل bursts قصيرة (فتح صفحة، تحميل feed) وبعدها فترات هدوء طويلة (قراءة، تفاعل).
التعريف العلمي والمصدر
Token Bucket Algorithm اتعرّف رسمياً في ATM Forum Traffic Management Specification 4.0 سنة 1996، وبعدها انتقل لعالم الـ networking في RFC 2697 (1999) و RFC 2698 (1999) كأساس لـ . الفكرة الجوهرية: بدل ما تحسب الطلبات في window زمني، احسب الـ ، اللي هي وحدات افتراضية بتمثّل "حق" تستهلك resource معيّن.