-
Notifications
You must be signed in to change notification settings - Fork 9.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(core): Reduce large relay message payloads to protect Redis #12342
fix(core): Reduce large relay message payloads to protect Redis #12342
Conversation
…uce-large-relay-message-payloads-to-protect-redis
Codecov ReportAttention: Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works nicely 👏 Couple comments about the implementation
@@ -1655,6 +1655,9 @@ | |||
"runData.aiContentBlock.tokens": "{count} Tokens", | |||
"runData.aiContentBlock.tokens.prompt": "Prompt:", | |||
"runData.aiContentBlock.tokens.completion": "Completion:", | |||
"runData.trimmedData.title": "Data too large to display", | |||
"runData.trimmedData.message": "The data is too large to be shown here. View the full details in 'Executions'.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"runData.trimmedData.message": "The data is too large to be shown here. View the full details in 'Executions'.", | |
"runData.trimmedData.message": "The data is too large to be shown here. View the full details in 'Executions' tab.", |
Maybe mention that it's a tab?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see 6723b15
packages/cli/src/constants.ts
Outdated
main: [ | ||
[ | ||
{ | ||
json: { isTrimmedManualExecutionDataItem: true }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could use a __
prefix something to mark it more clearly as something "internal"
json: { isTrimmedManualExecutionDataItem: true }, | |
json: { __isTrimmedManualExecutionDataItem: true }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see 2750822
* Stand-in for a manual execution data item too large to be sent live via pubsub. | ||
* This signals to the client to direct the user to the execution history. | ||
*/ | ||
export const TRIMMED_TASK_DATA_CONNECTIONS: ITaskDataConnections = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should move this to either core
or workflow
package so we could share the logic with FE instead of relying on magic strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see 17ff5a1
if (this.shouldRelayViaPubSub(pushRef)) { | ||
this.relayViaPubSub(pushMsg, pushRef); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO this logic shouldn't be in the Push
class. Now it ties the logic to implementation details of the scaling mode, where as Push should only be concerned about communicating via the websocket (or SSE). Now since this has already been here before we don't have to move it elsewhere in this PR. I just wanted to mention this for future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, we need to work on the lifecycle hooks first to unlock this refactor.
packages/cli/src/push/index.ts
Outdated
if (type === 'nodeExecuteAfter') pushMsgCopy.data.data.data = TRIMMED_TASK_DATA_CONNECTIONS; | ||
if (type === 'executionFinished') pushMsgCopy.data.rawData = ''; // prompt client to fetch from DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (type === 'nodeExecuteAfter') pushMsgCopy.data.data.data = TRIMMED_TASK_DATA_CONNECTIONS; | |
if (type === 'executionFinished') pushMsgCopy.data.rawData = ''; // prompt client to fetch from DB | |
if (type === 'nodeExecuteAfter') pushMsgCopy.data.data.data = TRIMMED_TASK_DATA_CONNECTIONS; | |
else if (type === 'executionFinished') pushMsgCopy.data.rawData = ''; // prompt client to fetch from DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see ba205b6
const maxMb = toMb(MAX_PAYLOAD_SIZE_BYTES); | ||
const { type } = pushMsgCopy; | ||
|
||
this.logger.warn(`Size of "${type}" (${eventMb} MB) exceeds max size ${maxMb} MB. Trimming...`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to also log the node that caused this. For that we would have to do the trimming already earlier in the chain to have that available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100%, same as here
{{ i18n.baseText('runData.trimmedData.message') }} | ||
</N8nText> | ||
<N8nButton size="small"> | ||
<a :href="`/workflow/${workflowsStore.workflowId}/executions`"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to do a full navigation that reload the entire page. Would it be possible to use vue router link?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case we can use the N8nRoute
component directly and style it how we wish
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, please see 33fc9f8
Summary
It is possible for a manual execution in scaling mode to cause so much data to flow through Redis that it becomes unresponsive. Specifically, the
nodeExecuteAfter
andexecutionFinished
events may carry arbitrarily large payloads.In this PR we replace payloads above a 5 MB limit with placeholders that signal the UI to direct the user to view the full payload in execution history instead. Events with payloads below the size limit continue to be sent and displayed as usual.
In future, we can override these placeholders with the full execution data fetched from the DB at the end of the execution. Since we are already planning to refactor execution data handling on the client in the near future, that change is out of scope for this PR.
Testing
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
docker exec -it <container> redis-cli monitor
localhost:3000
serving a 30 MB payloadFetch large payload
node have been trimmedRecording: https://share.cleanshot.com/GCrCsPXw
Related Linear tickets, Github issues, and Community forum posts
https://linear.app/n8n/issue/PAY-2403/reduce-large-relay-message-payloads-to-protect-redis
Review / Merge checklist
Docs updated or follow-up ticket createdTests includedPR labeled withrelease/backport