PgDog؛ یه کانکشنپولر که مجبورت نمیکنه کدت رو عوض کنی
خلاصهٔ کاملتر
نویسنده PgDog رو یه پراکسی برای مقیاسدادن به پستگرس معرفی میکنه که یکی از قابلیتهاش کانکشنپولینگه؛ یعنی اجازه میده کلی اپلیکیشن کلاینت از یه دیتابیس استفاده کنن بدون اینکه از سقف تعداد کانکشن رد بشن. سؤالی که مطرح میکنه اینه: با وجود پولرهای زیادی مثل PgBouncer، RDS Proxy، Pgpool-II و Supavisor، چرا یکی دیگه بسازیم؟
جوابشون مفهوم «انتزاع نشتیدار» (leaky abstraction)ه. به گفتهٔ نویسنده اکثر ابزارهای اکوسیستم پستگرس فلسفهٔ یونیکس رو جدی میگیرن (یه کار رو خوب انجام بده) و همین هم PgBouncer رو محبوب کرده. ولی وقتی PgBouncer یا RDS Proxy رو دیپلوی میکنی، سریع میفهمی یه تیکه زیرساخت اضافه کردی که نحوهٔ استفادهات از دیتابیس رو عوض میکنه و مجبورت میکنه ترید-آف بدی؛ اغلب با تغییر کد اپلیکیشن، که وقتی سالهاست داری اپت رو میسازی یعنی تغییر هزاران خط کد پروداکشن با تستکاوریج کم.
اولین چیزی که با اضافهشدن پولر از دست میره، کنترل نشسته یعنی دستورهای SETه. این دستورها برای بازنویسی موقت تنظیمات دیتابیس بهکار میرن؛ مثلاً برای اجرای کوئریهای کندتر:
SET statement_timeout TO '5m';
SELECT * FROM users WHERE banned IS true;نویسنده توضیح میده چون پولرها کانکشنها رو بین کلاینتها بازاستفاده میکنن، حالتِ کانکشنِ یه کلاینت به کلاینت دیگه «نشت» میکنه. تو بهترین حالت یه کوئری کند یه مدت اجرا میشه و باعث یه حادثهٔ دیتابیسی میشه؛ تو بدترین حالت سیاست امنیت سطح ردیف (RLS) که به یه متغیر نشسته وابستهست از کار میافته و ردیفها بیسروصدا ناپدید میشن. برای همین توصیهٔ رایج موقع مهاجرت به پولر اینه که دیگه SET رو استفاده نکن؛ ولی این خودش یه ترید-آفه و اگه برای چیز مهمی مثل RLS ازش استفاده میکنی، اصلاً نمیتونی پولینگ داشته باشی.
راهحل PgDog یه پارسر SQL داخلیه که دستورهای SET رو تشخیص میده، اسم و مقدار متغیرها رو درمیاره و روی هر کانکشن کلاینت تو پراکسی ذخیره میکنه. وقتی کلاینت یه کوئری میزنه، PgDog اول چک میکنه حالتش با سرور بخونه و اگه نخونه، با یه سری SET خودش بهروزش میکنه؛ اگه چند متغیر فرق داشته باشن با پایپلاین کردن کوئریها همه رو تو یه رفتوبرگشت آپدیت میکنه تا اثرش روی کارایی کم بمونه.
قابلیت دوم LISTEN/NOTIFYه که یه صف انتشار/اشتراک (pub/sub) داخل خود پستگرسه و بدون اضافهکردن دیتابیس دیگه به استکت خوب کار میکنه؛ ولی اینم چیزیه که تو حالت تراکنشی مجبور بودی کنار بذاری. PgDog این رو هم داخلی هندل میکنه و پیامها رو بین چند پروسهٔ PgDog جابهجا میکنه؛ برای کلاینت انگار PgDog کارگزار (broker)ه، ولی در واقع هنوز پستگرسه. به گفتهٔ نویسنده تمام معناشناسی تراکنشی NOTIFY هم حفظ میشه. پیادهسازیش از کانال broadcast تو Tokio برای جابهجایی پیام تو یه پروسه استفاده میکنه و برای چند پروسه، همهٔ دستورها رو از یه کانکشن اختصاصی به پستگرس میفرسته.
نکتهٔ آخر چندنخی بودنه. PgDog روی Tokio (رانتایم async رست با ورکرهای چندنخی) ساخته شده و هر کلاینت با تسک async خودش هندل میشه که خطی با تعداد کانکشنها مقیاس میگیره. نویسنده میگه برخلاف PgBouncer با SO_REUSEPORT و اتواسکیلینگ RDS Proxy که مجبورت میکنن پولها رو «شارد» کنی و کلاینت بعد از اتصال نمیتونه اینستنس عوض کنه، PgDog با استفاده از چند CPU ترافیک بیشتری هندل میکنه، بار ناگهانی کوئریها رو بدون منتظرموندن برای اتواسکیل جواب میده و مدیریتش هم سادهتره. به گفتهٔ اونها PgDog بیشتر از یه سال تو پروداکشن با ۲ میلیون کوئری در ثانیه کار کرده و متنباز و رایگانه.
نکات کلیدی:
- PgDog یه پراکسی مقیاسپذیری پستگرس با قابلیت کانکشنپولینگه
- ادعای اصلی: پولرهای فعلی «انتزاع نشتیدار»ن و مجبورت میکنن کد و روش کارت رو عوض کنی
- با یه پارسر SQL داخلی، دستور SET رو تشخیص میده و حالت هر کلاینت رو حفظ میکنه
- LISTEN/NOTIFY رو حتی تو حالت تراکنشی با کمک کانال broadcast تو Tokio کار میکنه
- روی Tokio چندنخیه، پس بدون شارد کردن پولها بار بیشتری رو هندل میکنه
- بیش از یه سال تو پروداکشن با ۲ میلیون کوئری در ثانیه، متنباز و رایگان




