مرحبا، أنا أشغل عدة عمال n8n يعالجون الوظائف من PostgreSQL. يقوم العمال بالمطالبة بالوظائف عن طريق تغيير حالتهم من قيد الانتظار إلى قيد المعالجة.
المشكلة هي أنه في بعض الأحيان يتعطل عامل أو يتم إعادة تشغيله بعد المطالبة بوظيفة. تبقى الوظيفة عالقة في المعالجة إلى الأبد ولا يتم التقاطها مرة أخرى.
أفكر في استخدام طابع زمني locked_at بحيث يمكن لعامل آخر تحديد الوظائف القديمة:
SELECT id, payload
FROM jobs
WHERE status = ‘processing’
AND locked_at < NOW() - INTERVAL ‘15 minutes’;
بعد ذلك يمكنني إعادة تعيين هذه الوظائف مرة أخرى إلى قيد الانتظار.
أنا مهتم بشكل أساسي بنمط يتعامل مع أعطال العمال بأمان دون إنشاء معالجة مكررة.
وصف المشكلة/الخطأ/السؤال
ما هي رسالة الخطأ (إن وجدت)؟
يرجى مشاركة سير العمل الخاص بك
(حدد العقد على لوحتك واستخدم اختصارات لوحة المفاتيح CMD+C/CTRL+C و CMD+V/CTRL+V لنسخ ولصق سير العمل.)
استخدم نموذج الإيجار/نبض القلب بدلاً من افتراض أن كل مهمة معالجة أكبر من 15 دقيقة قد انتهت.
سأعطي كل مهمة مطالب بها طابع زمني locked_at وويفضل أيضاً worker_id. يمكن للعمال تحديث نبض القلب بشكل دوري أثناء المعالجة.
للاسترجاع، سأستخدم تحديثاً ذرياً بحيث لا يمكن لعاملين استرجاع نفس المهمة:
UPDATE jobs
SET status = ‘pending’,
locked_at = NULL,
worker_id = NULL
WHERE id = $1
AND status = ‘processing’
AND locked_at < NOW() - INTERVAL ‘15 minutes’
RETURNING id;
إذا أرجعت الاستعلام صفاً، فقد استرجع العامل بنجاح. إذا لم ترجع شيئاً، فقد تعامل معها عامل آخر بالفعل أو لم تعد المهمة مؤهلة.
للمهام طويلة الأجل، سأفضل بالتأكيد نبض القلب على المهلة الزمنية الثابتة:
مطالبة المهمة
↓
status = processing
↓
تحديث نبض القلب
↓
معالجة المهمة
↓
وضع علامة كمكتملة
ثم سيكون لدي سير عمل استرجاع صغير ينظر بشكل دوري عن المهام التي انتهت مهلة نبض قلبها.
إذا توقف العامل عن العمل، ستنتهي صلاحية الإيجار في النهاية ويمكن لعامل آخر أن يتولى المهمة.
سأجعل أيضاً معالجة المهمة الفعلية متكاملة بذاتها وأحتفظ بعداد إعادة محاولة، حتى تتمكن مهمة تفشل بشكل متكرر من الانتقال في النهاية إلى قائمة الرسائل غير القابلة للتسليم بدلاً من إعادة المحاولة إلى الأبد.
مرحباً @Mary_Berry
عقد الإيجار في جدولك لا يمكنه تجديد نفسه بالطريقة التي يفعلها n8n. يحتفظ n8n بعقد إيجار مدته 60 ثانية على التنفيذ ويرفعه كل 10 ثوانٍ على مؤقت، لكن التنفيذ عبارة عن تشغيل مباشر عبر العُقد بدون خيط خلفي، لذا لا شيء يلمس locked_at بينما عقدة HTTP Request محظورة. التجديد يحدث فقط حيث تعمل عقدة، مما يعني حلقة Loop Over Items مع عقدة تجديد بداخلها. أي شيء آخر يجب تحديد حجمه بدلاً من نبض القلب..
تحديد الحجم يعني التوفيق بين ساعتين. الساعة التي تقرر أن عاملك ميت هي ساعة n8n، وليست ساعتك، والعامل الذي يكون مشغولاً جداً لتجديد نفسه خلال 60 ثانية يتم إعلان تنفيذه متوقفاً بينما الصف لا يزال مميزاً باسمك. على الإصدار 2.x لا يعيد تسليم التنفيذ، لذا فهو اختفى والصف ينتظر مدة 15 دقيقة كاملة لديك. ضع عقد إيجار n8n فوق أبطأ وظيفة لديك، ثم عقد إيجار وظيفتك فوق ذلك:
QUEUE_WORKER_LOCK_DURATION=900000
مع ترتيب معكوس، يسلم الحصاد الوظيفة إلى عامل ثان بينما الأول لا يزال يقوم بتشغيلها.