كيفية منع عمال n8n من معالجة نفس مهمة PostgreSQL بشكل متزامن؟

مرحباً بالجميع
أقوم بتشغيل عدة عمال n8n يسحبون الوظائف المعلقة من نفس جدول PostgreSQL. في ظل حركة المرور العادية يعمل بشكل جيد، لكن عندما يستعلم عدة عمال عن الجدول في نفس الوقت، قد يتم التقاط نفس الوظيفة من قبل أكثر من عامل واحد.
أنا حالياً أقوم بـ:
SELECT id, payload
FROM jobs
WHERE status = ‘pending’
ORDER BY created_at
LIMIT 10;
ثم أقوم بتحديث الوظائف المحددة إلى معالجة جارية.
أفكر في تغيير هذا إلى:
SELECT id, payload
FROM jobs
WHERE status = ‘pending’
ORDER BY created_at
FOR UPDATE SKIP LOCKED
LIMIT 10;
ما هو أفضل نمط للإنتاج للمطالبة الذرية بالوظيفة في n8n + PostgreSQL؟
هل يجب أن أستخدم FOR UPDATE SKIP LOCKED، أم UPDATE … RETURNING ذري، أم مزيجاً من كليهما؟
أيضاً، كيف يجب أن أتعامل مع الوظائف عندما يتعطل عامل بعد المطالبة بها حتى يمكن إعادة محاولتها بأمان دون إنشاء معالجة مكررة؟

صف المشكلة/الخطأ/السؤال

ما رسالة الخطأ (إن وجدت)؟

يرجى مشاركة سير العمل الخاص بك

(حدد العقد على لوحة الرسم واستخدم اختصارات لوحة المفاتيح CMD+C/CTRL+C و CMD+V/CTRL+V لنسخ ولصق سير العمل.)

شارك الإخراج الذي يتم إرجاعه بواسطة العقدة الأخيرة

معلومات حول إعداد n8n الخاص بك

  • إصدار n8n:
  • قاعدة البيانات (الافتراضي: SQLite):
  • إعداد n8n EXECUTIONS_PROCESS (الافتراضي: own, main):
  • تشغيل n8n عبر (Docker, npm, n8n cloud, تطبيق سطح المكتب):
  • نظام التشغيل:

سأفعل كليهما، فقط لكي أكون آمناً، شيء من هذا القبيل قد يساعد!

مرحبا @Mary_Berry بالنسبة لهذه الحالة، استخدم FOR UPDATE SKIP LOCKED أو UPDATE … RETURNING ذرّي، مع مهلة زمنية للعمال المعطلين.

الهدف الرئيسي هو جعل المطالبة بوظيفة ذرّية، بحيث لا يمكن لعاملي n8n اثنين الاستيلاء على نفس الوظيفة.

المطالبة بالوظائف بطريقة ذرّية
النهج الجيد هو:
WITH jobs_to_claim AS (
SELECT id
FROM jobs
WHERE status = ‘pending’
ORDER BY created_at
FOR UPDATE SKIP LOCKED
LIMIT 10
)
UPDATE jobs
SET
status = ‘processing’,
locked_at = NOW(),
worker_id = $1
FROM jobs_to_claim
WHERE jobs.id = jobs_to_claim.id
RETURNING jobs.*;

إذا توقف عامل ما عن العمل، يمكن لعامل آخر أن يستعيد الوظيفة في النهاية.

الحماية من العمال القدامى

عند إكمال وظيفة، تحقق من أن العامل لا يزال يمتلكها:
UPDATE jobs
SET status = ‘completed’
WHERE id = $1
AND status = ‘processing’
AND worker_id = $2;

إذا حدّثت 0 صفوف، فإن هذا العامل لم يعد يمتلك الوظيفة ولا يجب أن يعدّلها.

المبدأ الأساسي هو: يجب أن تتحكم قاعدة البيانات بملكية الوظيفة، وليس عمال n8n أنفسهم.

مرحباً @Mary_Berry أهلاً وسهلاً!
في وضع الطابور، يمكن أن يأتي التكرار من n8n نفسه، قبل تشغيل استعلام المطالبة. كل عامل يحتفظ بـ Redis lease على التنفيذ لمدة QUEUE_WORKER_LOCK_DURATION (60000 ms) وينجزه كل QUEUE_WORKER_LOCK_RENEW_TIME. العامل الذي يتعطل، أو ببساطة مشغول جداً لتجديده في الوقت المناسب، يتم وضع علامة على تنفيذه كمتوقف إذا أعيد تسليمه إلى عامل آخر، الذي يعيد تشغيل سير العمل من المشغل ويطالب بدفعة جديدة بينما تجلس الدفعة المتعطلة في processing. على n8n 1.x يحدث هذا التشغيل مرة واحدة بشكل افتراضي، لذا أوقفه وقم بتعيين الـ lease أعلى من أبطأ دفعة لديك:

QUEUE_WORKER_MAX_STALLED_COUNT=0
QUEUE_WORKER_LOCK_DURATION=300000

على n8n 2.0 هذا المتغير وإعادة محاولة Bull المتوقفة قد اختفيا، لذا احذف السطر الأول بعد الترقية وقم بقيادة استرجاع تلك الصفوف من الجدول الخاص بك.