နားလည်ထားရမယ့် အချက်
External client တွေဆီကနေ request လက်ခံတဲ့ service တိုင်းမှာ client တစ်ဦးတည်း — malicious ဖြစ်ဖြစ်၊ ရိုးရိုး bug ရှိလို့ဖြစ်ဖြစ် — backend တစ်ခုတည်းကို share သုံးနေတဲ့ တခြားသူတွေအတွက် performance ဆိုးအောင် လုပ်နိုင်လောက်အောင် request တွေကို မြန်မြန် ပို့တာကို ကာကွယ်ဖို့ လိုအပ်ပါတယ်။ Rate limiting က client တစ်ဦးအနေနဲ့ အချိန်ကန့်သတ်ချက်တစ်ခုအတွင်း request ဘယ်နှစ်ခု ပို့နိုင်မလဲဆိုတာကို ကန့်သတ်ပါတယ်။ Token bucket algorithm ဟာ ဒါကို implement လုပ်ဖို့ လူကြိုက်များတဲ့ နည်းလမ်းတစ်ခုပါ — ဘာလို့ဆိုတော့ ယှဉ်ပြိုင်နေတဲ့ လိုအပ်ချက် နှစ်ခုကို သဘာဝကျကျ ဟန်ချက်ညီအောင် လုပ်ပေးလို့ပါ — legitimate ဖြစ်တဲ့ ခဏတာ burst activity (user တစ်ဦးက page အနည်းငယ်ကို မြန်မြန် click လုပ်တာမျိုး) ကို ခွင့်ပြုပြီး long-run average rate ကိုလည်း တင်းကျပ်စွာ ချမှတ်ထားနိုင်ပါတယ်။ Bucket တစ်ခုက fixed capacity အထိ token ကို ကိုင်ထားနိုင်ပါတယ်၊ token တွေက fixed rate (ဥပမာ 2 per second) နဲ့ အဆက်မပြတ် refill ဖြစ်နေပါတယ်၊ ဝင်လာတဲ့ request တစ်ခုချင်းစီက ဆက်သွားဖို့ token တစ်ခု ကုန်ကျရပါတယ်။ Bucket ပြည့်နေရင် burst request တွေအားလုံးကို capacity အထိ ချက်ချင်း ဝန်ဆောင်နိုင်ပါတယ်။ Bucket ရိုက်ကုန်သွားတာနဲ့တော့ request တွေက refill rate ထက် မမြန်ဘဲ ရောက်လာအောင် throttle ဖြစ်သွားပါတယ်။ ဒါက naive fixed-window counter (ဥပမာ 'တစ်မိနစ်ကို max 100 requests') နဲ့ ကွာခြားပါတယ် — ဒီနည်းလမ်းက client တစ်ဦးအား window တစ်ခုရဲ့ နောက်ဆုံး 1 second မှာ request 100 ပို့ပြီး နောက် window ရဲ့ ပထမ 1 second မှာ ထပ်ပြီး 100 ပို့ခွင့်ပြုနိုင်ပါတယ် — token bucket ကတော့ ဒီ request 200 burst ကို ချောမွေ့အောင် လုပ်ပေးပါလိမ့်မယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Thuta Learning ရဲ့ API က third-party integration တွေကို ဖွင့်ပေးလာတဲ့အခါ — lesson progress ကို sync လုပ်ပေးတဲ့ partner app၊ quiz result ပြသတဲ့ browser extension — platform က caller တိုင်းကို ကောင်းမွန်စွာ ကျင့်ကြံမယ်လို့ ယုံကြည်လို့မရတော့ပါဘူး။ Configuration မှားနေတဲ့ integration တစ်ခုက lessons endpoint ကို 10 second တစ်ခါအစား 10 millisecond တစ်ခါ poll လုပ်ရင် database ကို ပြည့်သိပ်စေပြီး တစ်ချိန်တည်းမှာ tutorial ကြည့်နေတဲ့ student အစစ်တွေအတွက် site ကို နှေးကွေးအောင် လုပ်နိုင်ပါတယ်။ API ရှေ့မှာ token bucket ထားလိုက်ခြင်းအားဖြင့် — API key တစ်ခုချင်းစီအတွက် burst capacity 20, per second 5 refill ဆိုပါစို့ — ပုံမှန် usage pattern တွေ (page တစ်ခုက resource အများကြီးကို တစ်ပြိုင်နက် load လုပ်တာမျိုး) ကို ချက်ချင်း ဖြတ်သန်းနိုင်ပြီး ထိန်းမနိုင်တဲ့ script တစ်ခုကတော့ platform တစ်ခုလုံးကို ဖြိုချမည့်အစား ခံနိုင်ရည်ရှိတဲ့ rate ထိ throttle ဖြစ်သွားပါလိမ့်မယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import time
class TokenBucket:
def __init__(self, capacity: int, refill_rate: float):
self.capacity = capacity # max tokens the bucket can hold
self.refill_rate = refill_rate # tokens added per second
self.tokens = capacity # start full
self.last_check = time.monotonic()
def _refill(self):
now = time.monotonic()
elapsed = now - self.last_check
self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
self.last_check = now
def allow_request(self) -> bool:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
return False
bucket = TokenBucket(capacity=5, refill_rate=2) # burst of 5, refills 2/sec
# Simulate 8 rapid requests with no delay (burst)
results = [bucket.allow_request() for _ in range(8)]
print("Rapid burst of 8:", results)
# Wait for tokens to refill, then try again
time.sleep(1.5)
print("After 1.5s wait:", bucket.allow_request())Script က ပထမဆုံး rapid request 5 ခု အောင်မြင်တာ (bucket အပြည့်ကုန်သွားတာ)၊ နောက် 3 ခု ပယ်ချခံရတာနဲ့ 1.5 second စောင့်ပြီးနောက် token 3 ခု refill ဖြစ်လာလို့ request တစ်ခု ထပ်အောင်မြင်သွားတာကို ပြသထားပါတယ်။၅ မိနစ် စမ်းကြည့်
TokenBucket class ကို total ပယ်ချခံရတဲ့ request အရေအတွက်ကို track လုပ်ပြီး print ထုတ်အောင် ပြင်ဆင်ပါ။ ပြီးရင် capacity=10, refill_rate=1 နဲ့ request 15 ခုကို ချက်ချင်း ပို့လိုက်တဲ့အခါ ဘယ်နှစ်ခု အောင်မြင်မလဲ၊ parameter တွေအရ ဘာကြောင့် ဒီအရေအတွက် သင့်လျော်လဲဆိုတာ စမ်းသပ်ပါ။
သတိလေးတစ်ချက်
Refill calculation အတွက် monotonic clock အစား wall-clock time.time() ကို သုံးတာ — system clock က နောက်ကို ခုန်သွားရင် (NTP adjustment) elapsed time က negative ဖြစ်နိုင်ပြီး bucket logic ပျက်သွားနိုင်ပါတယ်၊ time.monotonic() ကတော့ clock ချိန်ညှိမှုတွေကနေ ကင်းလွတ်ပါတယ်။
Refill ပြီးနောက် token ကို capacity အထိ cap လုပ်ဖို့ မေ့သွားတာ — min(self.capacity, ...) မပါရင် ကြာကြာ idle နေတဲ့ client တစ်ခုက token အကန့်အသတ်မရှိ စုဆောင်းနိုင်ပြီး rate limiter က တားဆီးဖို့ ရည်ရွယ်ထားတဲ့ ကြီးမားတဲ့ burst ကို ပေါက်ကွဲစေနိုင်ပါတယ်။
Wikipedia — Rate limiting — System Design