مشكلة حلقة أخرى - 2.000 عنصر في ... 21.000 عنصر خارج

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

لدي حلقة داخل حلقة. سير العمل الخاص بي ينشئ 2000 عنصر وحجم مجموعة الحلقة الخارجية هو 100. الحلقة الداخلية لها حجم مجموعة يبلغ 1. تم معالجة تشغيل المجموعة الأولى من الحلقة الخارجية كما هو متوقع، لكن الحلقة الثانية تتصرف بطريقة غريبة جداً. في النهاية، أحصل على 21000 عنصر من الحلقة الخارجية. ما المشكلة هنا أم أن هذا خطأ ما؟

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

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

شارك النتيجة التي تُرجعها العقدة الأخيرة

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

  • إصدار n8n: 2.23.2 Community Edition
  • قاعدة البيانات (الافتراضية: SQLite):
  • إعداد n8n EXECUTIONS_PROCESS (الافتراضي: own, main):
  • تشغيل n8n عبر (Docker, npm, n8n cloud, desktop app):
  • نظام التشغيل: Ubuntu 22.04
  1. قطع الاتصال الذي يعود من الحلقة الخارجية إلى الحلقة الخارجية على العناصر.
  2. ضع عقدة Code مباشرة بعد انتهاء حلقتك الداخلية (على فرع “Done”)، قبل عودتها إلى الحلقة الخارجية.
  3. استخدم عقدة الكود هذه لتمرير البيانات، لكن قم بتكوين الحلقة الداخلية لإعادة تعيينها. في الإصدارات الأحدث من عقدة Split In Batches / Looping، يمكنك تبديل خيار Reset إلى true.
    آمل أن يكون هذا مفيداً

السلام عليكم @Nadir

المشكلة هي أن “الأسلاك” في سير العمل الخاص بك متصلة بالمنافذ الخاطئة. في n8n، عقدة Split in Batches لها مخرجان: واحد للعناصر قيد المعالجة حالياً (الحلقة) وواحد لعند انتهاء كل شيء (مسار Done). لقد قمت بالخلط بينهما عن طريق الخطأ، مما يعني أن سير العمل الخاص بك يحاول بدء الخطوة التالية فقط بعد انتهائه بالفعل، وهو يعود للخلف بطرق لا ينبغي أن يعود بها.

بسبب هذه الاتصالات المتبادلة، لقد أنشأت “حلقة تغذية راجعة”. بدلاً من معالجة كل عنصر مرة واحدة، يقوم سير العمل بتشغيل دورات جديدة من الحلقة في كل مرة ينتهي فيها دفعة. هذا يتسبب في تضاعف العناصر بشكل كبير، وهذا هو السبب في أن العناصر الأصلية البالغة 2,000 تحولت إلى 21,000.

لإصلاح هذا، تأكد من أن المخرج العلوي (Loop) لـ Outer Loop يدخل إلى Inner Loop، والمخرج العلوي لـ Inner Loop يتعامل مع العمل الفعلي الخاص بك. يجب أن يشير المخرج السفلي (Done) لـ Inner Loop إلى الخلف إلى Outer Loop لطلب الدفعة التالية. هذا يضمن انتهاء الحلقة الداخلية من جميع عناصرها قبل تقدم الحلقة الخارجية.

جرب هذا

مرحبا! شكراً لك على تخصيص الوقت للنظر في هذا الموضوع.

سير عملي بعد نصيحتك - هل فهمت شيئاً بشكل خاطئ؟:

إذا قمت بتفعيل خيار reset على Inner Loop Over Items فأنا أنتهي في حلقة لا نهائية. أتمكن من الخروج من هذا فقط بإعادة تشغيل نظام N8N.

{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-144,
-64
],
"id": "dded6d9b-9f8d-4227-8fc5-12159d18723b",
"name": "When clicking 'Execute workflow'"
},
{
"parameters": {},
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
912,
-64
],
"id": "6bcb6624-8761-4f94-879c-2dc3cfb6fe7b",
"name": "Wait",
"webhookId": "f3fe0b63-6c82-49eb-8380-d60c3f1541c6"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
672,
96
],
"id": "d7112c4a-6db2-4a14-98d1-f5c218502e8a",
"name": "Inner loop"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1120,
128
],
"id": "08aed799-7b15-4f38-be6e-35f74f432366",
"name": "Outer loop"
},
{
"parameters": {
"options": {
"reset": true
}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
448,
32
],
"id": "acf6c806-01e4-4b38-8aac-14f488408e89",
"name": "Inner Loop Over Items"
},
{
"parameters": {
"batchSize": 100,
"options": {}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
240,
-32
],
"id": "05ed47b8-8ecc-4313-8702-e3cf2ddb1adb",
"name": "Outer Loop Over Items"
},
{
"parameters": {
"jsCode": "const items = ;\n\nfor(let i = 1; i <= 2000; i++) {\n items.push({\n json: {\n id: i,\n }\n })\n}\n\nreturn items;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
32,
-64
],
"id": "8af1c78b-59a6-42df-a269-2f3c36dbcc5a",
"name": "Create Items"
},
{
"parameters": {
"jsCode": "// قم بالحلقة على عناصر الإدخال وأضف حقل جديد يسمى 'myNewField' إلى JSON لكل منها\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
-64
],
"id": "7676d152-fae2-4a54-94ef-4b3c685b1ed6",
"name": "Code in JavaScript"
}
],
"connections": {
"When clicking 'Execute workflow'": {
"main": [
[
{
"node": "Create Items",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Outer loop",
"type": "main",
"index": 0
}
]
]
},
"Inner loop": {
"main": [
[
{
"node": "Inner Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Outer loop": {
"main": [
[
{
"node": "Outer Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Inner Loop Over Items": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
],
[
{
"node": "Inner loop",
"type": "main",
"index": 0
}
]
]
},
"Outer Loop Over Items": {
"main": [
[ ], [ { “node”: “Inner Loop Over Items”, “type”: “main”, “index”: 0 } ] ] }, “Create Items”: { “main”: [ [ { “node”: “Outer Loop Over Items”, “type”: “main”, “index”: 0 } ] ] }, “Code in JavaScript”: { “main”: [ [ { “node”: “Wait”, “type”: “main”, “index”: 0 } ] ] } }, “pinData”: {}, “meta”: { “instanceId”: “3a5f2016f235f0058559dcaa60a889f9bd210e06a6a8d1adec8c4d5747d8d207” } }`

أعتذر بشدة، يمكنك استخدام الحل المقدم من قبل kjooleng أو لدي حل آخر وهو إنشاء سير عمل آخر.
إنشاء سير عمل ثانٍ:

  • ابدأ بـ Execute Workflow Trigger.

  • قم بربطه بـ Split In Batches (حجم الدفعة: 1).

  • ضع منطق Inner loop الخاص بك هنا.

  • عندما ينتهي هذا سير العمل، فإنه يدمر حالته تلقائياً، مما يعني أنه طازج تماماً في كل مرة يتم استدعاؤه.

  1. عدّل سير العمل الرئيسي الخاص بك (الحلقة الخارجية):
    • احتفظ بـ Outer Loop Over Items (حجم الدفعة: 100).

    • داخل الحلقة الخارجية، بدلاً من استخدام عقد الحلقة الداخلية القديمة، استخدم عقدة Execute Workflow التي تستدعي سير العمل الثاني الخاص بك.

    • مرر الدفعة الحالية المكونة من 100 عنصر إليها.

هذا لا يعمل على الإطلاق. ستقدم الحلقة الخارجية الدفعة الأولى عبر مخرج الحلقة إلى عقدة الحلقة الخارجية، وبما أنه لا توجد طريقة للعودة إلى الحلقة الخارجية عبر Items، فلن يحدث شيء. لن يتم الوصول إلى الحلقة الداخلية من خلال هذا الإعداد.

ربما يجب أن أشرح السبب في احتياجي لهذا الإعداد. لدي أكثر من 20000 عنصر وأحتاج إلى إرسالها عبر طلب HTTP إلى واجهة برمجية خارجية. هذه الواجهة البرمجية الخارجية يمكنها التعامل مع 999 عنصر كحد أقصى فقط لكل استدعاء. لذلك أحتاج إلى معالجة 20000 عنصر على دفعات. يجب أن تترجم الحلقة الداخلية العناصر (بحد أقصى 999) إلى الصيغة الصحيحة وأن تستدعي الواجهة البرمجية.

@SE-automations لا أحب ذلك، لكنني سأحاول استخدام سير عمل فرعي.

مرحباً @Nadir

ربما هذا هو ما تريده

@kjooleng يُرجع طلب HTTP نتيجة مستند XML يحتوي على 999 عنصر. بعد طلب HTTP أقوم بتحويل XML إلى JSON بـ 999 عنصر. والآن أحتاج إلى أن تكرر الحلقة الداخلية على هذه 999 عنصر قبل أن أعالج الدفعة التالية (999 عنصر التالية). في الأساس أحتاج إلى تحليل نتيجة طلب HTTP قبل أن أشغل طلب HTTP التالي. هذا هو السبب في أنني بحاجة إلى الحلقة الداخلية كجزء من الحلقة الخارجية. سأحاول استخدام سير العمل الفرعي.

ومع ذلك - أعتقد أن هناك خللاً في عقدة الحلقة.

وضع الحلقة الداخلية في سير عمل منفصل واستدعاؤها داخل الحلقة الخارجية يعمل بفعالية.

سعيد بأنك وجدت الحل :smiley:

بالنظر إلى سير العمل لديك، المشكلة واضحة. مخرجات الحلقة الداخلية متصلة بعودة إلى عقدة الحلقة الخارجية، مما يعني أنه في كل مرة تنتهي الحلقة الداخلية من معالجة دفعة ما، فإنها تعيد نتائجها إلى تدفق العناصر الخاص بالحلقة الخارجية. هذا هو السبب الدقيق وراء انتهائك بـ 21,000 عنصر بدلاً من 2,000.

الاتصال من الحلقة الداخلية بعودة إلى عقدة “الحلقة الخارجية على العناصر” يجب ألا يكون موجوداً. الشيء الوحيد الذي يجب أن يعود إلى عقدة “الحلقة الخارجية على العناصر” هو اتصال الحلقة نفسه، وليس المخرجات المعالجة.

إليك كيفية إصلاح ذلك:

أزل الاتصال القادم من مخرجات الحلقة الداخلية بعودة إلى عقدة “الحلقة الخارجية على العناصر”.

عقدة مخرجات الحلقة الخارجية يجب أن تكون مخرجاتك النهائية، وأي شيء يخرج من هناك هو نتيجتك النهائية بعد معالجة جميع الدفعات.

إذا كنت بحاجة إلى جمع جميع النتائج في النهاية، أضف عقدة دمج (Merge node) بعد مخرجات الحلقة الخارجية لدمج كل شيء بشكل نظيف.

بشكل أساسي، يجب أن تقود الحلقة الخارجية الدفعات، وتعالج الحلقة الداخلية كل عنصر ضمن تلك الدفعة، وتتدفق النتائج للأمام إلى مخرجات الحلقة الخارجية، وليس بعودة إلى بداية الحلقة الخارجية مرة أخرى.

أصلح هذا الاتصال وسيكون عدد العناصر لديك صحيحاً تماماً.

@akingbade-Samuel مرحباً! لا أفهم ما تقصده. الحلقة الوحيدة التي تعود إلى الحلقة الخارجية على العناصر تأتي من الحلقة الداخلية على العناصر عند الإخراج.

تحدث مضاعفة العناصر لأن حلقة “Loop Over Items” الداخلية تجمع العناصر عبر دفعاتها داخليًا، وعندما يتم تشغيل مخرجات “Done” الخاصة بها، يمرر n8n جميع العناصر المتراكمة مرة أخرى إلى إدخال الحلقة الخارجية. ثم تحمل كل تكرار دفعة خارجية كومة متزايدة من العناصر من التشغيلات السابقة.

الحل: على عقدة “Loop Over Items” الداخلية، قم بتفعيل خيار “Reset”. هذا يمسح عداد الحلقة الداخلية كل مرة يتم استدعاؤها من الحلقة الخارجية، لذلك تبدأ كل دفعة خارجية من جديد مع عناصرها الخاصة فقط، وليس المجموع التراكمي من جميع الدفعات السابقة.

إذا كان Reset مفعلًا بالفعل واستمرت المشكلة، شارك لقطة شاشة لإعدادات عقد الحلقة - سيساعد ذلك في تضييق الخيارات.

كما ذكرت أعلاه، إذا قمت بتشغيل إعادة التعيين على الحلقة الداخلية، ينتهي بي الحال في حلقة داخلية لا نهاية لها لا يمكن إيقافها إلا بإعادة تشغيل الخادم. الحل باستخدام سير العمل الفرعي يعمل وهو بشكل أساسي نفس التوصيل. سير العمل الفرعي أيضاً يُرجع 100 عنصر ولا يتم إضافة هذه العناصر إلى الحلقة الخارجية.

@nguyenthieutoan يمكن رؤية الإعدادات في سير العمل المرفوع. فقط انقر نقراً مزدوجاً على العقدة.

مرحبا! هذا ليس خللاً، بل مشكلة معروفة مع الحلقات المتداخلة في n8n.

المشكلة: عندما تنتهي حلقتك الداخلية، فإنها تمرر عناصرها مرة أخرى إلى الحلقة الخارجية في كل تكرار. لذلك بدلاً من أن تنتقل الحلقة الخارجية فقط إلى الدفعة التالية، فإنها تجمع جميع العناصر من تشغيلات الحلقة الداخلية، وهذا هو السبب في أنك تنتهي بـ 21,000 بدلاً من 2,000.

الحل: أضف عقدة Summarize أو عقدة Code مباشرة قبل أن تتغذى الحلقة الداخلية مرة أخرى في الحلقة الخارجية لإعادة تعيين/مسح عدد العناصر. بشكل أساسي، تحتاج إلى التأكد من أن العناصر المتوقعة فقط يتم تمريرها للأعلى.

تحقق أيضاً من أن مخرج “done” في حلقتك الداخلية متصل بشكل صحيح - يجب أن يتصل للأمام فقط، وليس عن طريق الخطأ يتغذى مرة أخرى في مدخل الحلقة الخارجية.

@Ayomikun27 إذا استخدمت سير عمل فرعي بدلاً من الحلقة الداخلية، فإن سير العمل الفرعي سيعيد أيضاً 100 عنصر. لكن هذه العناصر الـ 100 لا تُضاف إلى الحلقة الخارجية. لذا يجب أن يكون هناك خلل ما.

{
“nodes”: [
{
“parameters”: {
“batchSize”: 999,
“options”: {}
},
“type”: “n8n-nodes-base.splitInBatches”,
“typeVersion”: 3,
“position”: [
3536,
1088
],
“id”: “1796ac9c-4527-4682-b7ce-be15fb995083”,
“name”: “احصل على 999 عنصر في المرة”
},
{
“parameters”: {
“url”: "your api ",
“options”: {}
},
“type”: “n8n-nodes-base.httpRequest”,
“typeVersion”: 4.4,
“position”: [
3760,
1168
],
“id”: “92c992a3-c11c-45d7-a24e-f3ab21507c26”,
“name”: “إرسال طلب HTTP مع 999 عنصر”
},
{
“parameters”: {
“options”: {
“reset”: “={{ $prevNode.name ===‘send HTTP req with 999 items’}}”
}
},
“type”: “n8n-nodes-base.splitInBatches”,
“typeVersion”: 3,
“position”: [
3952,
1200
],
“id”: “81b34dbd-a826-4561-ad84-812a634dddfa”,
“name”: “حلقة على استجابة تلك الـ 999 عنصر”
},
{
“parameters”: {},
“type”: “n8n-nodes-base.noOp”,
“typeVersion”: 1,
“position”: [
4176,
1296
],
“id”: “9c9bb1f7-0417-4fa1-b0b3-ce0e474e3d32”,
“name”: “قم بعملياتك معهم”
},
{
“parameters”: {
“jsCode”: “const items = ;\n\nfor(let i = 1; i \u003c= 2000; i++) {\n items.push({\n json: {\n id: i,\n }\n })\n}\n\nreturn items;”
},
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
3328,
1056
],
“id”: “3a6ef3b8-3c88-48ab-b64f-bcc20b17990b”,
“name”: “احصل على بياناتك”
},
{
“parameters”: {
“options”: {}
},
“type”: “n8n-nodes-base.set”,
“typeVersion”: 3.4,
“position”: [
3744,
992
],
“id”: “21223cf6-52b9-430c-951b-84a1e2471274”,
“name”: “تم”
}
],
“connections”: {
“Get 999 items at a time”: {
“main”: [
[
{
“node”: “Done”,
“type”: “main”,
“index”: 0
}
],
[
{
“node”: “send HTTP req with 999 items”,
“type”: “main”,
“index”: 0
}
]
]
},
“send HTTP req with 999 items”: {
“main”: [
[
{
“node”: “loop over those 999 item’s response”,
“type”: “main”,
“index”: 0
}
]
]
},
“loop over those 999 item’s response”: {
“main”: [
[
{
“node”: “Get 999 items at a time”,
“type”: “main”,
“index”: 0
}
],
[
{
“node”: “Do your operation with them”,
“type”: “main”,
“index”: 0
}
]
]
},
“Do your operation with them”: {
“main”: [
[
{
“node”: “loop over those 999 item’s response”,
“type”: “main”,
“index”: 0
}
]
]
},
“Get your data”: {
“main”: [
[
{
“node”: “Get 999 items at a time”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“instanceId”: “0f39d8fdd402ddce20d0eb724526828e8c2d8679170e3a08fe6cd471c799fab6”
}
}

جرب هذا. فقط استخدم إعادة التعيين مع وضع «التعبير» وهذا البيان في حلقتك الداخلية. أخبرني إذا كان هذا يعمل.

الرقم 21,000 هو المؤشر الفاضح. هذا يساوي 100+200+300…+2000 وهو رقم مثلثي. ما يحدث هو أنه في كل مرة ينتهي الحلقة الداخلية ويتغذى مرة أخرى إلى الحلقة الخارجية، يجمع n8n جميع العناصر المرئية السابقة بدلاً من مجرد الدفعة الحالية. بحلول التكرار الخارجي الـ 20، تكون تحمل معك 2,000 عنصر.

هذا سلوك معروف مع عقد SplitInBatches المتداخلة — فهي تشارك حالة التنفيذ بطريقة تسبب تراكم العناصر.

الحل: انقل حلقتك الداخلية إلى سير عمل منفصل واستدعها باستخدام عقدة Execute Workflow. مرّر كل دفعة خارجية كمدخل، شغّل الحلقة الداخلية هناك، أعد النتائج. هذا يعزل حالات الحلقة الاثنتين تماماً.

سير العمل الخارجي: SplitInBatches (100) > Execute Workflow
سير العمل الداخلي: يستقبل 100 عنصر، يشغّل SplitInBatches (1) عليها، يعيد النتائج

هذا يتطلب إعدادات أكثر قليلاً لكنها النمط الموثوق للحلقات المتداخلة في n8n.