Skip to content
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

Conversation

ivov
Copy link
Contributor

@ivov ivov commented Dec 23, 2024

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 and executionFinished 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

  • Start a main in scaling mode with OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
  • Start a worker
  • Monitor Redis: docker exec -it <container> redis-cli monitor
  • Start an ad hoc server (see Slack) at localhost:3000 serving a 30 MB payload
  • Import workflow (see Slack), run it manually and call the test webhook
  • Verify that worker is processing manual execution and relaying to main
  • Verify that items after the Fetch large payload node have been trimmed
  • Verify in execution history that those items are displayed in full

Recording: 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

  • PR title and summary are descriptive.
  • Docs updated or follow-up ticket created
  • Tests included
  • PR labeled with release/backport

@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team ui Enhancement in /editor-ui or /design-system labels Dec 23, 2024
Copy link

codecov bot commented Dec 23, 2024

Codecov Report

Attention: Patch coverage is 27.08333% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/cli/src/push/index.ts 24.00% 19 Missing ⚠️
packages/editor-ui/src/components/RunData.vue 20.00% 16 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@tomi tomi left a 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'.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see 6723b15

main: [
[
{
json: { isTrimmedManualExecutionDataItem: true },
Copy link
Contributor

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"

Suggested change
json: { isTrimmedManualExecutionDataItem: true },
json: { __isTrimmedManualExecutionDataItem: true },

Copy link
Contributor Author

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 = {
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see 17ff5a1

Comment on lines +105 to +106
if (this.shouldRelayViaPubSub(pushRef)) {
this.relayViaPubSub(pushMsg, pushRef);
Copy link
Contributor

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

Copy link
Contributor Author

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.

Comment on lines 173 to 174
if (type === 'nodeExecuteAfter') pushMsgCopy.data.data.data = TRIMMED_TASK_DATA_CONNECTIONS;
if (type === 'executionFinished') pushMsgCopy.data.rawData = ''; // prompt client to fetch from DB
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Copy link
Contributor Author

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...`);
Copy link
Contributor

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

Copy link
Contributor Author

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`">
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the styling of N8nLink does not match what Giulio requested:

Capture 2024-12-31 at 09 49 30@2x

The button is too big and the color is not available in the N8nLink themes.

Copy link
Contributor

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

Copy link
Contributor Author

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

@ivov ivov requested a review from tomi December 31, 2024 08:51
@ivov ivov merged commit f52802a into offload-manual-executions-to-workers Dec 31, 2024
31 checks passed
@ivov ivov deleted the pay-2403-reduce-large-relay-message-payloads-to-protect-redis branch December 31, 2024 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team ui Enhancement in /editor-ui or /design-system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants