سطل سوراخ: از ساعت آبی بابلی تا کنترل ترافیک Postgres
خلاصهٔ کاملتر
سؤال سادهای که این مقاله از PlanetScale باهاش شروع میکنه اینه: این تابع باید اجرا بشه یا نه؟ به سازوکارهایی که جواب این سؤال رو میدن admission control میگن (یعنی کنترل اینکه چه کاری اجازهٔ ورود داره). یکی از معروفترین پیادهسازیهاش leaky bucket یا سطل سوراخه. نویسنده تاریخچهش رو از ساعتهای آبی بابلیها و مصریها (حدود ۱۶۰۰ پیش از میلاد) تا اولین استفادهٔ این اصطلاح تو شبکه توسط Jonathan Turner در ۱۹۸۶ دنبال میکنه.
تو نرمافزار، هر کار یه وزن داره و با حجم فعلی سطل مقایسه میشه. اگه جا نباشه، کار رد میشه؛ اگه باشه، وزنش به سطل اضافه میشه. سطل با گذشت زمان خالی میشه، نه با تموم شدن کار. دو حالت داره: بهعنوان meter تعیین میکنه چقدر کار همین الان اجرا بشه، و بهعنوان queue تعیین میکنه چقدر کار منتظر بمونه. شاپیفای تو GraphQL Admin API از همین برای rate limiting استفاده میکنه و هزینهٔ هر کوئری رو بر اساس چیزی که درخواست میکنه حساب میکنه.
نکتهٔ جالب اینه که سطل خیلی ارزونه. هیچ پروسهای پشت صحنه سطل رو خالی نمیکنه؛ فقط دو مقدار نگه داشته میشه: آخرین بار چقدر پر بوده و کِی. هر وقت لازم شد، سطح فعلی اینطوری حساب میشه:
level_now = max(0, level_then - drain_rate × (now - then))یعنی سطل فقط موقع چک کردن خالی میشه و سطل خالی اصلاً حافظهای نمیگیره.
به گفتهٔ نویسنده، یکی از رایجترین خرابیهای دیتابیس تو production اینه که نمیتونه کاری که از پسش برنمیاد رو آگاهانه رد کنه. Oracle از ۱۹۹۹ با Resource Manager و SQL Server از ۲۰۰۸ با Resource Governor چیزی شبیه این دارن، ولی Postgres و MySQL نه. Postgres تنظیماتی مثل statement_timeout داره که فقط بعد از مصرف منابع، کوئری رو قطع میکنه.
Traffic Control که PlanetScale تو ۲۰۲۶ عرضه کرده، با «resource budget» کار میکنه. هر بودجه روی کوئریهایی با تگ مشخص (از کامنت کوئری، کاربر یا اپلیکیشن) اعمال میشه و یا هشدار میده یا بلاکشون میکنه. سه تنظیم اصلی داره: server share و burst limit که یه token bucket میسازن (token bucket همون سطل سوراخه با استعارهٔ برعکس: سطل پر از توکنه و هر کار توکن خرج میکنه)، per-query limit برای سقف مصرف یه کوئری، و maximum concurrent workers.
چون دیتابیس قبل از اجرا هزینهٔ کوئری رو نمیدونه، Traffic Control اونو از روی query plan و سابقهٔ کوئریهای مشابه تخمین میزنه و بعد از اجرا اختلاف رو برمیگردونه یا کم میکنه. نرخ پر شدن بر حسب درصد ظرفیت سرور حساب میشه، چون یه ثانیه با ۸ هسته با یه ثانیه با یه هسته فرق داره. توصیهٔ PlanetScale اینه که بودجهٔ جدید رو اول تو حالت هشدار بسازید، ترافیک رو ببینید، اپ رو برای کوئریهای بلاکشده آماده کنید و بعد حالتشو عوض کنید.
نکات کلیدی:
- leaky bucket فقط دو مقدار نگه میداره: سطح قبلی و زمان آخرین چک، و بدون پروسهٔ پسزمینه کار میکنه.
- Postgres با statement_timeout فقط بعد از مصرف منابع کوئری رو قطع میکنه و جلوگیری پیش از اجرا نداره.
- Traffic Control سه تنظیم داره: server share و burst limit، per-query limit، و maximum concurrent workers.
- هزینهٔ کوئری از روی query plan و سابقهٔ کوئریهای همشکل تخمین زده میشه و بعد از اجرا اصلاح میشه.
- یه کوئری ممکنه زیر چند بودجه باشه و فقط وقتی اجرا میشه که شرط همهشون رو داشته باشه.
- شاپیفای تو GraphQL Admin API هم از leaky bucket برای rate limiting استفاده میکنه.




