Laravel 13.31 قابلیت جاب‌های قابل توقف (interruptible jobs) را اضافه می‌کند که به ورکر‌های صف (queue workers) اجازه می‌دهد سیگنال SIGTERM ارسال شده در طول استقرارها (deployments) را دریافت کرده و به جای نیمه‌تمام رها کردن یک تسک، به شکلی منظم (cleanly) خاموش شوند. تیم‌هایی که جاب‌های طولانی اجرا می‌کنند — مانند پردازش ده‌ها هزار ردیف، ارسال ایمیل‌های انبوه یا تولید گزارش‌ها — از این ویژگی بهره می‌برند، زیرا هنگام انتشار نسخه جدید، یکپارچگی داده‌ها حفظ می‌شود.

چرا استقرارها باعث از بین رفتن جاب‌ها می‌شدند

وقتی نسخه جدیدی مستقر می‌شود، مدیریت‌کننده‌های فرآیند (process managers) مانند Supervisor، Docker و Kubernetes با ارسال SIGTERM (درخواستی محترمانه برای پایان دادن به کار)، به ورکر‌های موجود دستور توقف می‌دهند. اکثر ورکرها قبل از خروج، منتظر می‌مانند تا جاب فعلی تمام شود. مشکل زمانی بروز می‌کند که مدیریت‌کننده تنها چند ثانیه برای رعایت این دستور فرصت می‌دهد. اگر یک جاب به چند دقیقه زمان نیاز داشته باشد، مدیریت‌کننده به SIGKILL تغییر وضعیت می‌دهد که باعث می‌شود فرآیند فوراً کشته شود. در این حالت، جاب هرگز به یک وضعیت یکپارچه نمی‌رسد و باعث ایجاد رکوردهای نیمه‌آپدیت شده، فایل‌های یتیم (orphaned files) یا کارهای تکراری می‌شود.

پاسخ Laravel: وقفه همکاری‌محور (cooperative interruption)

Laravel 13.31 قراردادی (contract) به نام Interruptible معرفی می‌کند که یک کلاس جاب می‌تواند آن را پیاده‌سازی کند تا از درخواست پایان کار مطلع شود. وقتی SIGTERM می‌رسد، Laravel متد interrupted() جاب را فراخوانی می‌کند. فریم‌ورک جاب را به طور خودکار متوقف نمی‌کند؛ بلکه جاب باید پرچمی (flag) را که Laravel تنظیم کرده است بررسی کرده و خودش خارج شود. این مدل همکاری‌محور به توسعه‌دهندگان اجازه می‌دهد تا تکرار فعلی حلقه (loop iteration) را تمام کنند، تغییرات جزئی را بازگردانند (roll back) یا قبل از خروج، یک نقطه بازگشت (checkpoint) ثبت کنند.

چگونه یک جاب را قابل توقف کنیم

  1. پیاده‌سازی قرارداد – عبارت implements Interruptible را به تعریف کلاس جاب اضافه کنید.
  2. بررسی پرچم در داخل حلقه کار – در هر تکرار، بررسی‌ای اضافه کنید تا ببینید آیا جاب باید متوقف شود یا خیر.
  3. انجام عملیات پاک‌سازی – در متد interrupted()، هر وضعیتی (state) را که اجازه می‌دهد جاب بعداً از همان‌جا ادامه یابد ذخیره کنید، یا وقفه را برای تحلیل‌های بعدی ثبت (log) کنید.

بزرگترین تله: از --once استفاده نکنید

اجرای ورکر صف با دستور php artisan queue:work --once مدیریت سیگنال را کاملاً غیرفعال می‌کند. ورکر یک جاب واحد را پردازش کرده و خارج می‌شود، اما هرگز هندلر SIGTERM را ثبت نمی‌کند. در نتیجه، هر جاب قابل توقفی درخواست پایان کار را نادیده گرفته و در میانه‌ی فرآیند کشته می‌شود. این الگو در کانتینرهای مبتنی بر cron و استقرارهای تک‌مرحله‌ای (one-shot) دیده می‌شود. برای رفع این مشکل، هر زمان که به جاب‌های قابل توقف نیاز دارید، از حالت دیمون (php artisan queue:work) استفاده کنید.

نظارت بر وقفه‌ها

Laravel دو رویداد (event) منتشر می‌کند که می‌توانید به آن‌ها متصل شوید (hook):

  • WorkerInterrupted – هر زمان که هر ورکری سیگنال SIGTERM را دریافت کند، منتشر می‌شود. ثبت این رویداد دید کلی از میزان تأثیر استقرارها بر ورکرها به شما می‌دهد.
  • JobInterrupted – تنها در صورتی منتشر می‌شود که جاب فعلی قرارداد Interruptible را پیاده‌سازی کرده باشد. از این رویداد برای اجرای پاک‌سازی‌های مخصوص جاب، مانند حذف فایل‌های موقت یا به‌روزرسانی نشانگر "آخرین ردیف پردازش شده"، استفاده کنید.

گوش دادن به این رویدادها به تیم‌ها اجازه می‌دهد داشبورد‌هایی بسازند که تأثیر استقرار را نشان داده و جاب‌هایی که مکرراً نیمه‌تمام می‌مانند را شناسایی کنند.

بهبود در گزارش‌دهی اندازه صف

این نسخه متد totalSize() را به مدیریت‌کننده صف اضافه می‌کند که تعداد کل جاب‌های در انتظار را در تمام صف‌ها برمی‌گرداند. این مقدار برای درایورهای مبتنی بر دیتابیس و Redis که تعداد واقعی را گزارش می‌دهند، قابل اعتماد است. درایورهایی مانند SQS، Sync و Beanstalkd همچنان مقدار صفر را برمی‌گردانند زیرا تعداد را از طریق API مربوط به Laravel فاش نمی‌کنند. تیم‌هایی که از SQS استفاده می‌کنند باید همچنان برای بررسی عمق صف به متریک‌های CloudWatch تکیه کنند.

این موضوع برای تنظیمات مختلف چه معنایی دارد

  • Docker/Kubernetes – اکنون می‌توان از دوره خاموش شدن منظم (graceful shutdown) به طور مؤثر استفاده کرد. دوره مهلت پایان کار (termination grace period) را افزایش دهید تا جاب‌ها چند ثانیه فرصت داشته باشند پرچم را تشخیص داده و به طور منظم خارج شوند.
  • Supervisor – مقدار stopwaitsecs را بیشتر از زمانی تنظیم کنید که انتظار دارید یک جاب پس از دریافت پرچم وقفه، تمام شود.
  • جاب‌های قدیمی (Legacy jobs) – هر جابی را که طولانی‌تر از زمان انتظار (timeout) مدیریت‌کننده اجرا می‌شود بازبینی کنید و در صورت امکان، آن را قابل توقف کنید.

نکته مقابل: پیچیدگی افزوده شده

این ویژگی جایگزین پیکربندی صحیح زمان انتظار (timeout) نمی‌شود. اگر حلقه یک جاب هرگز پرچم وقفه را بررسی نکند، مدیریت‌کننده همچنان SIGKILL ارسال خواهد کرد. تیم‌ها باید مسیرهای کد طولانی‌مدت را بازبینی کرده و در نقاط توقف منطقی (logical breakpoints)، بررسی‌های لازم را اضافه کنند. برخی از توسعه‌دهندگان ممکن است نوشتن کدهای اضافی (boilerplate) — یعنی پیاده‌سازی یک قرارداد و افزودن بررسی‌های پرچم — را برای جاب‌های کوتاهی که در هر صورت در بازه زمانی خاموش شدن تمام می‌شوند، دشوار یا غیرضروری بدانند.

چک‌لیست اقدامات

  1. اسکریپت‌های استقرار را برای یافتن پرچم --once اسکن کنید و در مواردی که از وظایف interruptible استفاده می‌شود، آن را با حالت daemon جایگزین کنید.
  2. طولانی‌ترین وظایف (آن‌هایی که ده‌ها هزار ردیف را تغییر می‌دهند یا چندین دقیقه اجرا می‌شوند) را شناسایی کرده و قرارداد Interruptible را به آن‌ها اضافه کنید.
  3. یک بررسی پرچم (flag check) را در داخل هر تکرار حلقه قرار دهید و هرگونه کد نهایی‌سازی لازم را به متد interrupted() منتقل کنید.
  4. شنودگرها (listeners) را به WorkerInterrupted و JobInterrupted متصل کنید تا متریک‌هایی را در مورد میزان تأثیر استقرارها بر فرآیند پردازش جمع‌آوری کنید.
  5. برای صف‌های Redis یا پایگاه داده، از totalSize() برای نظارت بر backlog استفاده کنید؛ برای SQS، هشدارهای CloudWatch را فعال نگه دارید.

با توقف‌های ناشی از استقرار، به جای یک قطع ناگهانی، مانند یک گام هماهنگ برخورد کنید. Laravel 13.31 یکپارچگی داده‌ها را حفظ کرده و دردسرهای عملیاتی مربوط به وظایف نیمه‌تمام را کاهش می‌دهد. هزینه این کار، افزایش اندک در مسئولیت‌پذیری کد است، اما تیم‌هایی که به پردازش‌های سنگین پس‌زمینه متکی هستند، به محیط production قابل‌اعتمادتری دست می‌یابند.