feat(16-05): implement hybrid batch update with updateContainers mutation
- Added IF node to check batch size (threshold: 5 containers) - Small batches (<=5): use single updateContainers mutation (parallel, fast) - Large batches (>5): use existing serial Execute Workflow loop - Build Batch Update Mutation node constructs updateContainers GraphQL query - Execute Batch Update with 120-second timeout for large image pulls - Handle Batch Update Response maps results and updates Container ID Registry - Format and send batch result via Telegram - Both paths produce consistent result messaging Workflow pushed to n8n successfully (HTTP 200).
This commit is contained in:
+214
-2
@@ -3371,7 +3371,15 @@
|
||||
"mode": "list",
|
||||
"value": "7AvTzLtKXM2hZTio92_mC"
|
||||
},
|
||||
"options": {}
|
||||
"options": {
|
||||
"timeout": 120000,
|
||||
"response": {
|
||||
"response": {
|
||||
"errorRedirection": "continue",
|
||||
"fullResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "720b6da1-60e6-4ec4-8a5f-d403ad05994c",
|
||||
"name": "Execute Batch Update",
|
||||
@@ -5291,6 +5299,137 @@
|
||||
2600,
|
||||
700
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "check-count",
|
||||
"leftValue": "={{ $json.totalCount }}",
|
||||
"rightValue": 5,
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "lte"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
}
|
||||
},
|
||||
"id": "if-check-batch-size",
|
||||
"name": "Check Batch Size",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2600,
|
||||
2600
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Build updateContainers (plural) mutation for small batches\nconst batchState = $input.item.json;\nconst containers = batchState.containers || [];\nconst chatId = batchState.chatId;\n\n// Get Container ID Registry to look up PrefixedIDs\nconst staticData = $getWorkflowStaticData('global');\nconst registry = JSON.parse(staticData._containerIdRegistry || '{}');\n\n// Build array of PrefixedIDs and name mapping\nconst ids = [];\nconst nameMap = {};\nconst notFound = [];\n\nfor (const container of containers) {\n const name = container.Name;\n const entry = registry[name];\n \n if (entry && entry.prefixedId) {\n ids.push(entry.prefixedId);\n nameMap[entry.prefixedId] = name;\n } else {\n // Container not in registry - this shouldn't happen but handle gracefully\n notFound.push(name);\n }\n}\n\nif (notFound.length > 0) {\n return {\n json: {\n error: true,\n errorMessage: `Containers not found in registry: ${notFound.join(', ')}`,\n chatId: chatId\n }\n };\n}\n\n// Build GraphQL mutation\nconst idsJson = JSON.stringify(ids);\nconst query = `mutation { docker { updateContainers(ids: ${idsJson}) { id state image imageId } } }`;\n\nreturn {\n json: {\n query: query,\n ids: ids,\n nameMap: nameMap,\n containerCount: ids.length,\n chatId: chatId,\n batchState: batchState\n }\n};\n"
|
||||
},
|
||||
"id": "code-build-batch-update-mutation",
|
||||
"name": "Build Batch Update Mutation",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
2800,
|
||||
2500
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $env.UNRAID_HOST }}/graphql",
|
||||
"authentication": "none",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "x-api-key",
|
||||
"value": "={{ $env.UNRAID_API_KEY }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ {\"query\": $json.query} }}",
|
||||
"options": {
|
||||
"timeout": 120000,
|
||||
"response": {
|
||||
"response": {
|
||||
"errorRedirection": "continue",
|
||||
"fullResponse": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "http-execute-batch-update",
|
||||
"name": "Execute Batch Update",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
3000,
|
||||
2500
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Handle updateContainers mutation response\nconst response = $input.item.json;\nconst prevData = $('Build Batch Update Mutation').item.json;\nconst chatId = prevData.chatId;\nconst nameMap = prevData.nameMap;\n\n// Check for GraphQL errors\nif (response.errors) {\n return {\n json: {\n success: false,\n error: true,\n errorMessage: response.errors[0].message,\n chatId: chatId,\n batchMode: 'parallel'\n }\n };\n}\n\n// Extract updated containers\nconst updated = response.data?.docker?.updateContainers || [];\n\nif (updated.length === 0) {\n return {\n json: {\n success: false,\n error: true,\n errorMessage: 'No containers returned from updateContainers mutation',\n chatId: chatId,\n batchMode: 'parallel'\n }\n };\n}\n\n// Map results back to container names\nconst results = updated.map(container => ({\n name: nameMap[container.id] || container.id,\n imageId: container.imageId,\n state: container.state,\n success: true\n}));\n\n// Update Container ID Registry with new IDs\nconst staticData = $getWorkflowStaticData('global');\nconst registry = JSON.parse(staticData._containerIdRegistry || '{}');\n\nupdated.forEach(container => {\n const name = nameMap[container.id];\n if (name && registry[name]) {\n registry[name].prefixedId = container.id;\n registry[name].unraidId = container.id;\n registry[name].lastSeen = Date.now();\n }\n});\n\nstaticData._containerIdRegistry = JSON.stringify(registry);\n\nreturn {\n json: {\n success: true,\n batchMode: 'parallel',\n updatedCount: results.length,\n results: results,\n chatId: chatId\n }\n};\n"
|
||||
},
|
||||
"id": "code-handle-batch-update-response",
|
||||
"name": "Handle Batch Update Response",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3200,
|
||||
2500
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Format batch update result for Telegram message\nconst result = $input.item.json;\nconst chatId = result.chatId;\n\nif (result.error) {\n return {\n json: {\n chatId: chatId,\n text: `\u274c Batch update failed: ${result.errorMessage}`,\n success: false\n }\n };\n}\n\nconst mode = result.batchMode || 'parallel';\nconst count = result.updatedCount || 0;\nconst results = result.results || [];\n\n// Build summary message\nlet message = `\u2705 Batch update complete (${mode} mode)\\n\\n`;\nmessage += `Updated ${count} container${count !== 1 ? 's' : ''}:\\n`;\n\nresults.forEach(r => {\n const icon = r.success ? '\u2713' : '\u2717';\n message += ` ${icon} ${r.name}\\n`;\n});\n\nreturn {\n json: {\n chatId: chatId,\n text: message,\n success: true\n }\n};\n"
|
||||
},
|
||||
"id": "code-format-batch-result",
|
||||
"name": "Format Batch Result",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
3400,
|
||||
2600
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "={{ $json.chatId }}",
|
||||
"text": "={{ $json.text }}",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"id": "telegram-send-batch-result",
|
||||
"name": "Send Batch Result",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1.2,
|
||||
"position": [
|
||||
3600,
|
||||
2600
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "I0xTTiASl7C1NZhJ",
|
||||
"name": "Telegram account"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
@@ -7332,7 +7471,80 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"registry-update-bitmap-stop": {}
|
||||
"registry-update-bitmap-stop": {},
|
||||
"code-prepare-update-all-batch": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "if-check-batch-size",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"if-check-batch-size": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "code-build-batch-update-mutation",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "code-prepare-batch-loop",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"code-build-batch-update-mutation": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "http-execute-batch-update",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"http-execute-batch-update": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "code-handle-batch-update-response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"code-handle-batch-update-response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "code-format-batch-result",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"code-format-batch-result": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "telegram-send-batch-result",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
|
||||
Reference in New Issue
Block a user