Rate Limiting

একবার ভাবো, তুমি একটি ওয়েবসাইট বা এপিআই (API) চালাচ্ছ, যেখানে প্রতি সেকেন্ডে লক্ষ লক্ষ রিকোয়েস্ট আসছে। যদি হঠাৎ এক বা একাধিক ইউজার অস্বাভাবিক পরিমাণ রিকোয়েস্ট পাঠাতে থাকে, তাহলে তোমার সার্ভার অতিরিক্ত লোডের কারণে ক্র্যাশ করতে পারে।

এই সমস্যার সমাধান হচ্ছে রেট লিমিটিং (Rate Limiting)। এটি নিশ্চিত করে যে, নির্দিষ্ট সময়ের মধ্যে একটি নির্দিষ্ট পরিমাণ রিকোয়েস্টই সার্ভার গ্রহণ করবে এবং এর চেয়ে বেশি হলে তা ব্লক (Block) বা ডিলে (Delay) করা হবে।

রেট লিমিটিং কী?

রেট লিমিটিং হলো একটি সিস্টেম ডিজাইন টেকনিক, যা কোনো ইউজার বা ক্লায়েন্ট নির্দিষ্ট সময়ের মধ্যে কতবার একটি রিকোয়েস্ট পাঠাতে পারবে, তা নিয়ন্ত্রণ করে। এটি মূলত API সিকিউরিটি, সার্ভার প্রোটেকশন এবং ফেয়ার ইউজেস পলিসি (Fair Usage Policy) বজায় রাখতে ব্যবহৃত হয়।

কেন রেট লিমিটিং গুরুত্বপূর্ণ?

  • সার্ভার ওভারলোড প্রতিরোধ: একসঙ্গে অনেক রিকোয়েস্ট আসলে সার্ভার ধীর হয়ে যেতে পারে বা ডাউন হয়ে যেতে পারে। রেট লিমিটিং এই ট্রাফিক কন্ট্রোল করে।

  • DDoS অ্যাটাক প্রতিরোধ: কেউ যদি ইচ্ছাকৃতভাবে বট (Bot) দিয়ে হাজার হাজার রিকোয়েস্ট পাঠায়, রেট লিমিটিং সেগুলোকে ব্লক করে সার্ভারকে বাঁচায়।

  • ফেয়ার ইউজেস নিশ্চিতকরণ: একজন ইউজার যেন একাই সার্ভারের সব রিসোর্স দখল করে না নেয়, তা নিশ্চিত করে অন্যান্য ইউজারদের জন্য সার্ভিস ঠিক রাখে।

  • কস্ট ম্যানেজমেন্ট (Cost Management): ক্লাউড সার্ভিস (যেমন: AWS, GCP) ব্যবহার করলে অতিরিক্ত রিকোয়েস্টের কারণে বিল অনেক বেড়ে যেতে পারে। রেট লিমিটিং খরচ নিয়ন্ত্রণে সাহায্য করে।

রেট লিমিটিংয়ের প্রধান অ্যালগরিদমসমূহ

রেট লিমিটিং ইমপ্লিমেন্ট করার জন্য সাধারণত ৪টি জনপ্রিয় অ্যালগরিদম ব্যবহার করা হয়:

১. Token Bucket Algorithm

এই পদ্ধতিতে, প্রত্যেক ইউজারের জন্য একটি লজিক্যাল বাকেট (বালতি) থাকে, যেখানে নির্দিষ্ট সংখ্যক টোকেন থাকে। প্রতিটি রিকোয়েস্ট প্রসেস করার জন্য একটি করে টোকেন খরচ হয়।

  • বাকেটে টোকেন থাকলে রিকোয়েস্ট অ্যাকসেপ্ট হয়।

  • বাকেট খালি হয়ে গেলে রিকোয়েস্ট রিজেক্ট হয়।

  • একটি নির্দিষ্ট সময় পরপর বাকেটে আবার নতুন টোকেন রিফিল (Refill) হয়।

Python ইমপ্লিমেন্টেশন:

import time

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        self.capacity = capacity
        self.tokens = capacity
        self.refill_rate = refill_rate # tokens per second
        self.last_refill_time = time.time()

    def allow_request(self):
        current_time = time.time()
        elapsed_time = current_time - self.last_refill_time
        
        # Refill tokens based on elapsed time
        self.tokens += elapsed_time * self.refill_rate
        self.tokens = min(self.capacity, self.tokens)
        self.last_refill_time = current_time

        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

# Testing: Bucket capacity is 10, refills 1 token per second
bucket = TokenBucket(capacity=10, refill_rate=1)

for i in range(12):
    if bucket.allow_request():
        print(f"Request {i+1}: Allowed [SUCCESS]")
    else:
        print(f"Request {i+1}: Blocked [FAILED]")

২. Leaky Bucket Algorithm

এটি অনেকটা টোকেন বাকেটের মতোই, তবে এখানে রিকোয়েস্টগুলো একটি কিউ (Queue) বা লাইনে জমা হয়। সার্ভার একটি নির্দিষ্ট ফিক্সড রেটে (Fixed rate) কিউ থেকে রিকোয়েস্ট প্রসেস করে। যদি হঠাৎ অনেক রিকোয়েস্ট চলে আসে এবং কিউ ভর্তি হয়ে যায়, তবে নতুন রিকোয়েস্টগুলো ড্রপ (Drop) বা রিজেক্ট হয়ে যায়। এটি ট্রাফিক স্মুথ করার জন্য দারুণ কাজ করে।

৩. Fixed Window Counter Algorithm

এই পদ্ধতিতে সময়কে নির্দিষ্ট উইন্ডোতে (যেমন: প্রতি ১ মিনিট) ভাগ করা হয়। প্রতিটি উইন্ডোর জন্য একটি কাউন্টার থাকে। উইন্ডোর শুরু থেকে রিকোয়েস্ট আসলে কাউন্টার বাড়ে। লিমিট পার হয়ে গেলে ওই উইন্ডোর বাকি সময়টুকুতে সব রিকোয়েস্ট ব্লক হয়। নতুন উইন্ডো শুরু হলে কাউন্টার আবার শূন্য (Zero) হয়ে যায়।

Python ইমপ্লিমেন্টেশন:

from collections import defaultdict
import time

class FixedWindowRateLimiter:
    def __init__(self, limit, window_size):
        self.limit = limit
        self.window_size = window_size
        self.requests = defaultdict(int)

    def allow_request(self, user_id):
        # Calculate the current window ID (e.g., current time / window size)
        current_window = int(time.time()) // self.window_size
        
        # Create a unique key by combining User ID and window ID
        key = f"{user_id}:{current_window}"
        
        if self.requests[key] < self.limit:
            self.requests[key] += 1
            return True
        return False

# Testing: Allows 5 requests per 10-second window
rate_limiter = FixedWindowRateLimiter(limit=5, window_size=10)

for i in range(7):
    if rate_limiter.allow_request("user_1"):
        print(f"Request {i+1}: Allowed [SUCCESS]")
    else:
        print(f"Request {i+1}: Blocked [FAILED]")

এই অ্যালগরিদমের একটি সমস্যা হলো 'Burst problem'। উইন্ডোর একদম শেষ মুহূর্তে এবং নতুন উইন্ডোর শুরুতে একসাথে অনেক রিকোয়েস্ট আসলে সার্ভার দ্বিগুণ লোড ফেস করতে পারে।

৪. Sliding Window (Log / Counter)

এটি ফিক্সড উইন্ডোর সমস্যার সমাধান। এখানে সময়কে ফিক্সড ব্লকে ভাগ না করে, রিকোয়েস্ট আসার সময় থেকে ঠিক পেছনের নির্দিষ্ট সময়টুকু (যেমন: গত ৬০ সেকেন্ড) হিসাব করা হয়। এর ফলে ট্রাফিক বার্স্ট (Traffic burst) খুব চমৎকারভাবে হ্যান্ডেল করা যায়।

রেট লিমিটিং কোথায় ব্যবহৃত হয়?

  • API Gateway: Nginx, Kong, AWS API Gateway ইত্যাদিতে রেট লিমিট কনফিগার করা থাকে।

  • ওয়েব অ্যাপ্লিকেশন: লগইন পেজে ব্রুট-ফোর্স (Brute-force) ঠেকাতে (যেমন: ৩ বার ভুল পাসওয়ার্ড দিলে ১৫ মিনিট ব্লক)।

  • CDN ও Load Balancer: Cloudflare, Akamai-এর মতো সার্ভিসগুলো ট্রাফিক ফিল্টার করতে এটি ব্যবহার করে।

বাস্তব জীবনের উদাহরণ

  1. লগইন লিমিট: তুমি যদি জিমেইল বা ফেসবুকে বারবার ভুল পাসওয়ার্ড দিয়ে লগইন করার চেষ্টা করো, তবে কিছুক্ষণ পর "Try again later" লেখা আসে।

  2. API লিমিট: OpenAI (ChatGPT), Twitter বা Google Maps-এর API ফ্রি টায়ার ইউজারদের জন্য নির্দিষ্ট করে দেয় যে তারা প্রতি মিনিটে সর্বোচ্চ কতটি রিকোয়েস্ট পাঠাতে পারবে (যেমন: 3 requests/min)।

  3. স্ট্রিমিং প্ল্যাটফর্ম: ইউটিউব বা নেটফ্লিক্স রেট লিমিটিং ব্যবহার করে, যাতে কোনো স্ক্রিপ্ট বা বট আনলিমিটেড ভিডিও রিকোয়েস্ট করে সার্ভার ডাউন করে দিতে না পারে।

সিস্টেম ডিজাইনে রেট লিমিটিং সঠিকভাবে প্রয়োগ করতে পারলে এটি তোমার আর্কিটেকচারের পারফরম্যান্স এবং সিকিউরিটি দুটোই বহুগুণ বাড়িয়ে দেবে।