اسکیل لگاریتمی برای Laravel Horizon
خلاصهٔ کاملتر
نویسنده تو یکی از پروژههای جانبیاش با Horizon حدود ۱۰۰ ورکر روی پنج سرور داره و در پیک حدود ۴۵۰۰ جاب در دقیقه پردازش میکنه. میگه مشکلش حجم نیست، الگوی بار ـه: یه صف نیمهشب ۱۰۰ هزار جاب میگیره و ۲۴ ساعت برای تموم کردنش وقت داره، یکی هر ساعت ۱۰ هزار جاب میگیره، و یکی هم یه جریان ثابت حدوداً یک جاب در ثانیه داره. همون صفهای کوچیکترن که ۲۰ دقیقه بیورکر موندن واقعاً اذیتشون میکنه.
چون runtime جابها تقریباً یکیه، نویسنده نمیخواست فقط به خاطر الگوی ورود، supervisorها رو از هم جدا کنه؛ پس از استراتژی size استفاده میکرد. اون استراتژی خیلی تحتاللفظیه: صف ۱۰۰ هزارتایی نسبت به صف ۱۰ هزارتایی حدود ده برابر وزن میگیره. نتیجهاش اینه که صف غول بلافاصله تقریباً همهٔ ورکرها رو برمیداره، بقیه روی حداقل یک ورکر میمونن و شروع میکنن به رشد. یعنی صف لحظهای اول باید خودش تبدیل به مشکل بشه تا Horizon بهش واکنش بده.
ایدهٔ راهحل سادهست: لگاریتم (یعنی هرچی عدد بزرگتر شه بازم بزرگ میشه، ولی خیلی کندتر) فاصلههای بزرگ رو فشرده میکنه. صف ۱۰۰ هزارتایی ۵۰ برابر صف ۲ هزارتاییه، ولی وزن لگاریتمیاش حتی دو برابر هم نمیشه. نویسنده با ۱۳ ورکر فرضی مقایسه میکنه: با size سهمها میشه حدود ۰.۲ و ۱.۲ و ۱۱.۶، ولی با لگاریتم میشه حدود ۳.۵ و ۴.۲ و ۵.۳. به زبون آدمیزاد یعنی چیزی نزدیک ۴/۴/۵ ورکر بهجای ۱/۱-۲/بقیه.
پیادهسازی عمداً کوچیکه. بهجای ساختن scaler یا strategy class جدید، فقط یه مقدار سوم به همون آپشن autoScalingStrategy اضافه شده: log. اول مجموع وزن لگاریتمی همهٔ صفها حساب میشه و بعد همون جای اندازهٔ خطی مینشینه:
$totalLogJobs = $queues->sum(fn ($queue) => log1p($queue['size']));
$numberOfProcesses = log1p($timeToClear['size']) / $totalLogJobs;دلیل استفاده از log1p اینه که log(0) به کار نمیآد و صف خالی چیز کاملاً عادیایه. log1p همون log(1 + x) ـه، پس صف خالی وزن صفر میگیره و صف پر وزن مثبت. پایهٔ لگاریتم هم بعد از نرمالسازی مهم نیست، چون عوض کردن پایه همهٔ وزنها رو در یه عدد ثابت ضرب میکنه و تو تقسیم حذف میشه. نویسنده عمداً از تعداد جاب استفاده کرده نه time-to-clear، چون واحد زمان داخل لگاریتم یه ثابت اضافه میکنه که دیگه حذف نمیشه.
تستها هم دقیقاً همون مشکل اصلی رو هدف گرفتن. تو یه سناریو با ۲۵ ورکر، دو صف با ۹۴۶ و ۱۳٬۷۰۲ جاب و یه صف خالی، استراتژی size بعد از یه پاس میرسه به ۲ و ۲۲ ورکر، ولی log میده ۱۱ و ۱۳. با این حال نویسنده تأکید میکنه log استراتژی بهتری نیست، فقط سومیه: تو یه pipeline پردازش رسانه که runtime جابها واقعاً فرق داره، time درستتره و فشرده کردن فاصلهها حتی میتونه بدتر باشه. PR این تغییر روی ریپوی Horizon بازه.
نکات کلیدی:
- مقدار جدید 'autoScalingStrategy' => 'log' کنار دو مقدار موجود time و size اضافه شده
- وزن هر صف با log1p($queue['size']) حساب میشه، پس صف خالی وزن صفر میگیره
- time بر اساس کار برآوردی، size بر اساس تعداد خام جاب، و log بر اساس اندازه ولی با کم کردن سلطهٔ بکلاگهای غول
- تو تست با ۲۵ ورکر، size میداد ۲ و ۲۲ ورکر؛ log میده ۱۱ و ۱۳
- Horizon ورکر کسری نمیده و تغییرها رو ترتیبی اعمال میکنه، پس عدد نهایی به وضعیت فعلی و حداقل پروسهها هم بستگی داره




