أنا أقوم ببناء سير عمل n8n يستقبل أحداث webhook من خدمة خارجية. قد تُنشئ السجلات نفسها أحداثاً متعددة، لكن في بعض الأحيان تصل بترتيب غير صحيح بسبب إعادة المحاولات أو تأخير الشبكة.
مثال: Order Updated → status = shipped
Order Updated → status = delivered
لكن n8n يستقبل delivered أولاً ثم shipped بعده.
تحديث PostgreSQL الحالي لدي هو ببساطة:
UPDATE orders
SET status = $2,
updated_at = NOW()
WHERE id = $1;
هل تنصحني باستخدام طابع زمني للحدث، أو رقم متسلسل، أو عمود نسخة حتى لا يتمكن حدث قديم من الكتابة فوق البيانات الأحدث؟
كيف يمكنك تنفيذ ذلك بأمان في PostgreSQL؟
وصف المشكلة/الخطأ/السؤال
ما رسالة الخطأ (إن وجدت)؟
يرجى مشاركة سير العمل الخاص بك
(حدد العُقد على لوحتك واستخدم اختصارات لوحة المفاتيح CMD+C/CTRL+C و CMD+V/CTRL+V للنسخ واللصق.)
شارك المخرجات المُرجعة من العُقدة الأخيرة
معلومات إعداد n8n الخاص بك
- إصدار n8n:
- قاعدة البيانات (الافتراضي: SQLite):
- إعداد n8n EXECUTIONS_PROCESS (الافتراضي: own، main):
- تشغيل n8n عبر (Docker، npm، n8n cloud، تطبيق سطح المكتب):
- نظام التشغيل:
@Gabby_Timothy استخدم رقم تسلسلي أو رقم إصدار كلما توفره واجهة برمجية التطبيقات. إنه أكثر أماناً بكثير من الاعتماد على الطوابع الزمنية فقط.
UPDATE orders
SET
status = $2,
version = $3,
updated_at = NOW()
WHERE id = $1
AND version < $3;
إذا وصل حدث أقدم لاحقاً، فإن شرط version < $3 يمنعه من الكتابة فوق الحالة الأحدث.
إذا كانت واجهة برمجية التطبيقات توفر الطوابع الزمنية فقط، يمكنك استخدام: UPDATE orders
SET status = $2,
updated_at = $3
WHERE id = $1
AND updated_at < $3;
قم أيضاً بتخزين الأحداث الواردة بشكل منفصل إذا كنت بحاجة إلى التدقيق أو إعادة التشغيل.
إذا استخدمت عمود version لمنع الأحداث الأقدم من الكتابة فوق الأحداث الأحدث، كيف ستتعامل مع حدثي webhook يصلان في الوقت نفسه تقريباً؟
هل version < $3 وحده كافٍ، أم يجب أن أستخدم أيضاً معاملة PostgreSQL أو قفل على مستوى الصف لجعل التحديث آمناً تماماً تحت تنفيذات n8n المتزامنة؟ @Keira_Becky
@Gabby_Timothy version < $3 عادة ما يكون كافياً لمنع حدث أقدم من الكتابة فوق حدث أحدث، لأن PostgreSQL تقيّم الشرط بشكل ذري.
UPDATE orders
SET
status = $2,
version = $3,
updated_at = NOW()
WHERE id = $1
AND version < $3;
إذا ضربت عمليتا تنفيذ n8n نفس الطلب في نفس الوقت تقريباً، فإن PostgreSQL تتعامل مع تحديث الصف بشكل متسلسل. وبمجرد كتابة الإصدار الأعلى، لن يعود التحديث الأقدم يلبي الشرط version < $3.
أضف FOR UPDATE فقط عندما تحتاج إلى تنفيذ عمليات قاعدة بيانات متعددة معتمدة على بعضها في نفس المعاملة.
مرحباً @Gabby_Timothy
تطابق UPDATE المحمية صفر صفوف عندما لا يكون الصف موجوداً بعد، يبلغ Postgres عن UPDATE 0 بدون خطأ، والحدث يختفي. التسليم غير المرتب يجعل هذه الحالة روتينية، لأن الحدث الذي ينشئ السجل قد يصل بعد الحدث الذي يحدثه. ادمج الإدراج والحماية في بيان واحد:
INSERT INTO orders (id, status, version, updated_at)
VALUES ($1, $2, $3, NOW())
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status, version = EXCLUDED.version, updated_at = NOW()
WHERE orders.version < EXCLUDED.version
RETURNING *;
id يحتاج إلى مفتاح أساسي أو فهرس فريد لكي يستهدف ON CONFLICT. الصف المفقود يتم إدراجه، والحدث الأحدث يتم تطبيقه، والحدث القديم يتم تجاهله، والـ < الصارم أيضاً يبتلع إعادة تشغيل دقيقة للإصدار ذاته. يقفل Postgres الصف المتعارض عند تشغيل DO UPDATE ويضمن نتيجة إدراج أو تحديث ذرية تحت التزامن، لذا هذا ينطبق على حالة الوصول المتزامن الخاصة بك أيضاً. RETURNING يسلم فقط صفاً كتبه البيان، والحدث القديم يعود فارغاً، لذا العقدة التالية يمكنها أن تتفرع على ما إذا تم تطبيق أي شيء.
في عقدة Postgres قم بتشغيله باستخدام Query Parameters واضبط Query Batching على Independently. Single Query يطوي جميع العناصر الواردة في بيان واحد، وحدثان لنفس الطلب في دفعة واحدة يفشلان مع “لا يمكن لأمر ON CONFLICT DO UPDATE التأثير على الصف مرة ثانية”.