Laravel 13.31 מוסיפה משימות שניתן להפסיק (interruptible jobs), מה שמאפשר לעובדי התור (queue workers) לתפוס את אות ה-SIGTERM שנשלח במהלך פריסות (deployments) ולסגור את התהליך בצורה מסודרת במקום לבטל משימה באמצע. צוותים שמריצים משימות ארוכות — עיבוד עשרות אלפים של שורות, שליחת אימיילים בכמות גדולה (bulk emails), או הפקת דוחות — יפיקו מכך תועלת מכיוון שהנתונים יישארו עקביים כאשר גרסה חדשה מופצת.

למה פריסות (deployments) הרסו משימות

כאשר גרסה חדשה מגיעה, מנהלי תהליכים כגון Supervisor, Docker ו-Kubernetes מורים לעובדים הקיימים לעצור על ידי שליחת SIGTERM, בקשה מנומסת לסיום התהליך. רוב העובדים ממתינים שהמשימה הנוכחית תסתיים לפני היציאה. הבעיה מתחילה כאשר מנהל התהליכים מעניק רק כמה שניות לביצוע הפעולה. אם משימה זקוקה לדקות, המנהל עובר ל-SIGKILL, שמסיים את התהליך באופן מיידי. המשימה לעולם לא מגיעה למצב עקבי, מה שמותיר רשומות שעודכנו חלקית, קבצים יתומים או עבודה כפולה.

הפתרון של Laravel: הפרעה שיתופית (cooperative interruption)

Laravel 13.31 מציגה חוזה (contract) בשם Interruptible שניתן לממש במחלקת משימה כדי שתהיה מודעת לבקשת הסיום. כאשר מגיע SIGTERM, Laravel קוראת למתודה interrupted() של המשימה. הפריימוורק לא עוצרת את המשימה באופן אוטומטי; על המשימה לבדוק דגל (flag) ש-Laravel מגדירה ולצאת בעצמה. מודל שיתופי זה מאפשר למפתחים לסיים את איטרציית הלולאה הנוכחית, לבצע rollback לשינויים חלקיים, או לכתוב נקודת בקרה (checkpoint) לפני היציאה.

איך להפוך משימה לניתנת להפסקה

  1. ממשו את החוזה – הוסיפו implements Interruptible להגדרת מחלקת המשימה.
  2. בדקו את הדגל בתוך לולאת העבודה – הכניסו בדיקה בכל איטרציה כדי לראות אם המשימה צריכה לעצור.
  3. בצעו ניקוי – ב-interrupted(), שמרו כל מצב (state) שיאפשר למשימה להתחדש מאוחר יותר, או רשמו (log) את ההפסקה לצורך ניתוח עתידי.

המלכודת הגדולה ביותר: אל תשתמשו ב---once

הרצת ה-queue worker עם הפקודה php artisan queue:work --once מבטלת לחלוטין את הטיפול באותות (signal handling). העובד מעבד משימה אחת ויוצא, אך הוא לעולם לא רושם את ה-SIGTERM handler. כתוצאה מכך, כל משימה שניתנת להפסקה מתעלמת מבקשת הסיום ונהרגת באמצע התהליך. דפוס זה מופיע בקונטיינרים המופעלים על ידי cron ובפריסות חד-פעמיות (one-shot deployments). תקנו זאת על ידי הרצת מצב daemon (php artisan queue:work) בכל פעם שאתם זקוקים למשימות ניתנות להפסקה.

מעקב אחר הפסקות

Laravel מפעילה שני אירועים (events) שניתן להצמיד אליהם לוגיקה:

  • WorkerInterrupted – מופק בכל פעם שעובד (worker) כלשהו מקבל SIGTERM. רישום אירוע זה נותן מבט כללי על התדירות שבה פריסות קוטעות עובדים.
  • JobInterrupted – מופק רק אם המשימה הנוכחית מממשת את חוזה ה-Interruptible. השתמשו באירוע זה כדי להפעיל ניקוי ספציפי למשימה, כגון מחיקת קבצים זמניים או עדכון סימון "השורה האחרונה שעובדה".

האזנה לאירועים אלו מאפשרת לצוותים לבנות לוחות בקרה (dashboards) המציגים את השפעת הפריסות ומזהים משימות שנתקעות לעיתים קרובות.

שיפור בדיווח על גודל התור

הגרסה מוסיפה מתודה totalSize() למנהל התור (queue manager), המחזירה את המספר הכולל של משימות ממתינות בכל התורים. הערך אמין עבור דרייברים מבוססי database ו-Redis, אשר מדווחים על ספירות בפועל. דרייברים כמו SQS, Sync ו-Beanstalkd עדיין מחזירים אפס מכיוון שהם אינם חושפים ספירה דרך ה-API של Laravel. צוותים המשתמשים ב-SQS צריכים להמשיך להסתמך על מדדי CloudWatch עבור עומק התור.

מה זה אומר עבור הגדרות שונות

  • Docker/Kubernetes – ניתן כעת להשתמש ביעילות בתקופת הסגירה המסודרת (graceful shutdown). האריכו את תקופת ה-grace period של הסיום כדי שלמשימות יהיו כמה שניות לשים לב לדגל ולצאת בצורה מסודרת.
  • Supervisor – הגדירו את stopwaitsecs לערך גבוה יותר מהזמן שבו אתם מצפים שהמשימה תסתיים לאחר קבלת דגל ההפסקה.
  • משימות Legacy – סקרו כל משימה שרצה זמן רב יותר מה-timeout של מנהל התהליכים, ובמידת האפשר, הפכו אותה לניתנת להפסקה.

נקודת מבט נגדית: מורכבות מוגברת

התכונה אינה מחליפה הגדרת timeout נכונה. אם לולאת המשימה לעולם לא תבדוק את דגל ההפסקה, מנהל התהליכים עדיין ישלח SIGKILL. על הצוותים לבצע audit לנתיבי קוד ארוכי-טווח ולהכניס בדיקות בנקודות עצירה לוגיות (logical breakpoints). חלק מהמפתחים עשויים למצוא את קוד התשתית הנוסף (boilerplate) — מימוש חוזה והוספת בדיקות דגל — כלא רלוונטי למשימות קצרות שכבר מסתיימות בתוך חלון הסגירה.

רשימת פעולות לביצוע

  1. Scan deployment scripts for the --once flag and replace it with daemon mode where interruptible jobs are used.
  2. Identify the longest-running jobs (those that touch tens of thousands of rows or run for several minutes) and add the Interruptible contract.
  3. Insert a flag check inside each loop iteration, and move any necessary finalisation code into the interrupted() method.
  4. Hook listeners to WorkerInterrupted and JobInterrupted to collect metrics on how often deployments affect processing.
  5. For Redis or database queues, use totalSize() to monitor backlog; for SQS, keep CloudWatch alarms in place.

Treat deployment-driven shutdowns as a coordinated step rather than an abrupt kill. Laravel 13.31 keeps data consistent and eases the operational headaches of half-finished jobs. The trade-off is a modest increase in code responsibility, but teams that rely on heavy background processing gain a more reliable production environment.