تضيف Laravel 13.31 وظائف قابلة للمقاطعة (interruptible jobs)، مما يسمح لعمال الطابور (queue workers) بالتقاط إشارة SIGTERM التي تُرسل أثناء عمليات النشر والإغلاق بشكل نظيف بدلاً من إلغاء المهمة في منتصف تنفيذها. تستفيد الفرق التي تشغل مهاماً طويلة — مثل معالجة عشرات الآلاف من الصفوف، أو إرسال رسائل البريد الإلكتروني الجماعية، أو إنشاء التقارير — لأن البيانات تظل متسقة عند طرح إصدار جديد.
لماذا كانت عمليات النشر تتسبب في إيقاف المهام
عندما يتم طرح إصدار جديد، يقوم مديرو العمليات مثل Supervisor و Docker و Kubernetes بإخبار العمال الحاليين بالتوقف عن طريق إرسال SIGTERM، وهو طلب مهذب للإنهاء. ينتظر معظم العمال انتهاء المهمة الحالية قبل الخروج. تظهر المشكلة عندما يمنح المدير بضع ثوانٍ فقط للامتثال. إذا كانت المهمة تحتاج إلى دقائق، فإنه يتصاعد إلى SIGKILL، مما يؤدي إلى قتل العملية فوراً. لا تصل المهمة أبداً إلى حالة متسقة، مما يترك سجلات محدثة جزئياً، أو ملفات يتيمة، أو تكراراً في العمل.
حل Laravel: المقاطعة التعاونية
تقدم Laravel 13.31 عقداً (contract) يسمى Interruptible يمكن لفئة المهمة (job class) تنفيذه لتصبح مدركة لطلب الإنهاء. عند وصول SIGTERM، تستدعي Laravel الدالة interrupted() الخاصة بالمهمة. لا يقوم الإطار بإيقاف المهمة تلقائياً؛ بل يجب على المهمة التحقق من علامة (flag) تضعها Laravel والخروج من تلقاء نفسها. يسمح هذا النموذج التعاوني للمطورين بإنهاء تكرار الحلقة الحالي، أو التراجع عن التغييرات الجزئية، أو كتابة نقطة تفتيش (checkpoint) قبل الخروج.
كيفية جعل المهمة قابلة للمقاطعة
- تنفيذ العقد – أضف
implements Interruptibleإلى تعريف فئة المهمة. - التحقق من العلامة داخل حلقة العمل – أدخل فحصاً في كل تكرار لمعرفة ما إذا كان يجب إيقاف المهمة.
- إجراء عمليات التنظيف – في الدالة
interrupted()، قم بحفظ أي حالة ستسمح للمهمة بالاستئناف لاحقاً، أو سجل المقاطعة للتحليل اللاحق.
الفخ الأكبر: لا تستخدم --once
إن تشغيل عامل الطابور باستخدام php artisan queue:work --once يعطل معالجة الإشارات تماماً. يقوم العامل بمعالجة مهمة واحدة ثم يخرج، لكنه لا يسجل أبداً معالج SIGTERM. وبناءً على ذلك، تتجاهل أي مهمة قابلة للمقاطعة طلب الإنهاء ويتم قتلها في منتصف العملية. يظهر هذا النمط في الحاويات التي تعمل بواسطة cron وعمليات النشر لمرة واحدة (one-shot deployments). قم بإصلاح ذلك عن طريق تشغيل وضع الـ daemon (php artisan queue:work) كلما احتجت إلى وظائف قابلة للمقاطعة.
مراقبة المقاطعات
تطلق Laravel حدثين يمكنك الارتباط بهما:
- WorkerInterrupted – يتم إطلاقه كلما تلقى أي عامل إشارة SIGTERM. يوفر تسجيل هذا الحدث رؤية عالية المستوى لمدى تكرار مقاطعة عمليات النشر للعمال.
- JobInterrupted – يتم إطلاقه فقط إذا كانت المهمة الحالية تنفذ عقد Interruptible. استخدم هذا الحدث لتشغيل عمليات تنظيف خاصة بالمهمة، مثل حذف الملفات المؤقتة أو تحديث علامة "آخر صف تمت معالجته".
يسمح الاستماع إلى هذه الأحداث للفرق ببناء لوحات تحكم توضح تأثير النشر وتحدد المهام التي يتم قطعها بشكل متكرر.
تحسين في تقارير حجم الطابور
يضيف الإصدار دالة totalSize() إلى مدير الطابور، والتي تعيد العدد الإجمالي للمهام المعلقة عبر جميع الطوابير. القيمة موثوقة لمحركات التشغيل (drivers) المدعومة بقواعد البيانات و Redis، والتي تعيد أعداداً فعلية. أما المحركات مثل SQS و Sync و Beanstalkd فلا تزال تعيد صفراً لأنها لا توفر عدداً من خلال واجهة برمجة تطبيقات Laravel. يجب على الفرق التي تستخدم SQS الاستمرار في الاعتماد على مقاييس CloudWatch لمعرفة عمق الطابور.
ماذا يعني هذا للإعدادات المختلفة
- Docker/Kubernetes – يمكن الآن استخدام فترة الإغلاق الآمن (graceful shutdown) بشكل فعال. قم بتمديد فترة السماح بالإنهاء حتى يكون لدى المهام بضع ثوانٍ لملاحظة العلامة والخروج بشكل نظيف.
- Supervisor – اضبط
stopwaitsecsلتكون أعلى من الوقت الذي تتوقع أن تستغرقه المهمة للانتهاء بعد تلقي علامة المقاطعة. - المهام القديمة (Legacy jobs) – راجع أي مهمة تستغرق وقتاً أطول من مهلة المدير، واجعلها قابلة للمقاطعة حيثما كان ذلك ممكناً.
وجهة نظر مقابلة: تعقيد إضافي
هذه الميزة لا تحل محل التكوين الصحيح للمهلة الزمنية (timeout). إذا لم تتحقق حلقة المهمة أبداً من علامة المقاطعة، فسيظل المدير يرسل SIGKILL. يجب على الفرق مراجعة مسارات الكود التي تستغرق وقتاً طويلاً وإدراج فحوصات عند نقاط التوقف المنطقية. قد يجد بعض المطورين أن الكود الإضافي (boilerplate) — المتمثل في تنفيذ عقد وإضافة فحوصات العلامة — غير جذاب للمهام القصيرة التي تنتهي بالفعل ضمن نافذة الإغلاق.
قائمة التحقق من الإجراءات
- افحص سكربتات النشر بحثاً عن علامة
--onceواستبدلها بوضع daemon حيث تُستخدم المهام القابلة للمقاطعة. - حدد المهام الأطول استغراقاً للوقت (تلك التي تتعامل مع عشرات الآلاف من الصفوف أو تستغرق عدة دقائق) وأضف عقد Interruptible.
- أدرج فحصاً للعلامة داخل كل تكرار للحلقة، وانقل أي كود إنهاء ضروري إلى دالة
interrupted(). - اربط المستمعين بـ
WorkerInterruptedوJobInterruptedلجمع المقاييس حول مدى تأثير عمليات النشر على المعالجة. - بالنسبة لطوابير Redis أو قواعد البيانات، استخدم
totalSize()لمراقبة التراكم؛ وبالنسبة لـ SQS، حافظ على تنبيهات CloudWatch كما هي.
تعامل مع عمليات الإيقاف الناتجة عن النشر كخطوة منسقة بدلاً من كونها إيقافاً مفاجئاً. يحافظ Laravel 13.31 على اتساق البيانات ويخفف من المتاعب التشغيلية الناتجة عن المهام غير المكتملة. الثمن هو زيادة طفيفة في مسؤولية الكود، ولكن الفرق التي تعتمد على المعالجة الخلفية الكثيفة ستكتسب بيئة إنتاج أكثر موثوقية.
