متد DB::olap() تو لاراول 13.36 برای کوئریهای سنگین PlanetScale
خلاصهٔ کاملتر
PlanetScale دیتابیسهای MySQL رو روی Vitess (یعنی یه لایه برای شاردینگ و مقیاسدادن MySQL) اجرا میکنه. حالت پیشفرضش OLTP هست، یعنی حالتی که برای کوئریهای کوتاه و روزمرهی اپلیکیشن ساخته شده. تو این حالت هر کوئری حداکثر ۱۰۰ هزار ردیف رو برمیگردونه یا تغییر میده و خروجیش نهایتاً ۶۴ مگابایته. پس یه خروجی CSV از یه جدول بزرگ که روی MySQL لوکال راحت اجرا میشه، اونجا خطا میده.
Vitess یه حالت دوم به اسم OLAP داره که این محدودیتها رو نداره. به گفتهی Laravel News، قبلاً باید دستی با DB::statement حالت رو عوض میکردی و تو یه بلوک finally برش میگردوندی. این روش دو تا مشکل داشت: دستور فقط روی اتصال write اجرا میشد و اگه read replica داشتی، select میرفت سراغ اتصالی که هنوز OLTP بود. از اون طرف چون این تنظیم مال session هست، اگه finally یادت میرفت، اتصال برای جاب بعدی تو صف یا درخواست بعدی تو Octane هم OLAP میموند.
حالا Jack Bayliss متد olap() رو به کلاس MySqlConnection اضافه کرده. این متد حالت رو عوض میکنه، callback رو اجرا میکنه، تو finally برمیگردونه روی OLTP و هر چی callback برگردونده رو پس میده. حالت رو هم روی اتصال write عوض میکنه هم read، و اگه اتصال read هنوز وصل نشده باشه اول وصلش میکنه:
$revenue = DB::olap(fn () => Order::query()
->selectRaw("customer_id, date_format(created_at, '%Y-%m') as month, sum(total) as revenue")
->where('created_at', '>=', now()->subYears(3))
->groupBy('customer_id', 'month')
->get()
);اگه دیتابیس Vitess نباشه، olap() فقط callback رو صدا میزنه، یعنی همین کد روی MySQL لوکال، CI و PlanetScale یکسان کار میکنه. callback خود اتصال رو هم میگیره که برای اتصالهایی غیر از پیشفرض مثل DB::connection('analytics') به درد میخوره. ولی چون متد فقط روی MySqlConnection تعریف شده، اگه اتصال پیشفرضت PostgreSQL یا SQLite باشه، BadMethodCallException میگیری. یه متد isVitess() هم اضافه شده که اگه نسخهی سرور شامل Vitess یا PlanetScale باشه true برمیگردونه.
یه نکتهی مهم: حالت درست وقتی callback تموم شد برمیگرده. متدهای cursor()، lazy() و lazyById() یه LazyCollection برمیگردونن که کوئریش موقع پیمایش اجرا میشه. پس اگه فقط اونو از callback برگردونی و بیرون پیمایشش کنی، کوئری تو حالت OLTP اجرا میشه و باز به محدودیت میخوری. باید پیمایش یا get() رو داخل خود callback انجام بدی.
نویسنده تأکید میکنه که خود PlanetScale استفاده از OLAP رو برای کوئریهای عادی اپ توصیه نمیکنه، چون محدودیتهای OLTP جلوی کوئریهایی رو میگیرن که منابع دیتابیس رو میخورن. پیشنهادش صفحهبندی یا کپی داده به یه دیتابیس تحلیلی جداست، و اگه اتصال قطع بشه کوئری OLAP ممکنه خطا بده. به گفتهی مقاله، جای درست DB::olap() خروجیهای گاهبهگاه، backfill یا گزارشهای ادمینه که باید بیشتر از ۱۰۰ هزار ردیف رو یهجا بخونن.
نکات کلیدی:
- محدودیت OLTP تو PlanetScale: حداکثر ۱۰۰ هزار ردیف و ۶۴ مگابایت برای هر کوئری
- DB::olap() از لاراول 13.36 اومده و تو PR شمارهی 61932 اضافه شده
- حالت رو روی هر دو اتصال read و write عوض میکنه و تو finally برمیگردونه روی OLTP
- روی MySQL غیر Vitess هیچ کاری نمیکنه، ولی روی PostgreSQL و SQLite خطای BadMethodCallException میده
- cursor() و lazy() و lazyById() رو باید داخل callback پیمایش کنی وگرنه کوئری تو OLTP اجرا میشه
- متد isVitess() هم با بررسی نسخهی سرور اضافه شده




