mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
fix(core): Make runner disconnected error more user-friendly (no-changelog) (#11829)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
1a8fb7bdc4
commit
ececdb09e4
|
@ -12,6 +12,10 @@ import type { DisconnectAnalyzer, DisconnectErrorOptions } from './runner-types'
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer {
|
export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer {
|
||||||
|
get isCloudDeployment() {
|
||||||
|
return config.get('deployment.type') === 'cloud';
|
||||||
|
}
|
||||||
|
|
||||||
async toDisconnectError(opts: DisconnectErrorOptions): Promise<Error> {
|
async toDisconnectError(opts: DisconnectErrorOptions): Promise<Error> {
|
||||||
const { reason, heartbeatInterval } = opts;
|
const { reason, heartbeatInterval } = opts;
|
||||||
|
|
||||||
|
@ -22,6 +26,9 @@ export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TaskRunnerDisconnectedError(opts.runnerId ?? 'Unknown runner ID');
|
return new TaskRunnerDisconnectedError(
|
||||||
|
opts.runnerId ?? 'Unknown runner ID',
|
||||||
|
this.isCloudDeployment,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { TaskRunnerDisconnectedError } from '../task-runner-disconnected-error';
|
||||||
|
|
||||||
|
describe('TaskRunnerDisconnectedError', () => {
|
||||||
|
it('should have the correct default error message', () => {
|
||||||
|
const error = new TaskRunnerDisconnectedError('test-runner-id', false);
|
||||||
|
|
||||||
|
expect(error.message).toBe('Node execution failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have the error level set to "error"', () => {
|
||||||
|
const error = new TaskRunnerDisconnectedError('test-runner-id', false);
|
||||||
|
|
||||||
|
expect(error.level).toBe('error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the correct description for non-cloud deployments', () => {
|
||||||
|
const error = new TaskRunnerDisconnectedError('test-runner-id', false);
|
||||||
|
|
||||||
|
expect(error.description).toContain(
|
||||||
|
'This can happen for various reasons. Please try executing the node again.',
|
||||||
|
);
|
||||||
|
expect(error.description).toContain(
|
||||||
|
'1. Reduce the number of items processed at a time, by batching them using a loop node',
|
||||||
|
);
|
||||||
|
expect(error.description).toContain(
|
||||||
|
"2. Increase the memory available to the task runner with 'N8N_RUNNERS_MAX_OLD_SPACE_SIZE' environment variable",
|
||||||
|
);
|
||||||
|
expect(error.description).not.toContain(
|
||||||
|
'Upgrade your cloud plan to increase the available memory',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the correct description for cloud deployments', () => {
|
||||||
|
const error = new TaskRunnerDisconnectedError('test-runner-id', true);
|
||||||
|
|
||||||
|
expect(error.description).toContain(
|
||||||
|
'This can happen for various reasons. Please try executing the node again.',
|
||||||
|
);
|
||||||
|
expect(error.description).toContain(
|
||||||
|
'1. Reduce the number of items processed at a time, by batching them using a loop node',
|
||||||
|
);
|
||||||
|
expect(error.description).toContain(
|
||||||
|
'2. Upgrade your cloud plan to increase the available memory',
|
||||||
|
);
|
||||||
|
expect(error.description).not.toContain(
|
||||||
|
"Increase the memory available to the task runner with 'N8N_RUNNERS_MAX_OLD_SPACE_SIZE' environment variable",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,7 +1,34 @@
|
||||||
|
import type { TaskRunner } from '@n8n/task-runner';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
export class TaskRunnerDisconnectedError extends ApplicationError {
|
export class TaskRunnerDisconnectedError extends ApplicationError {
|
||||||
constructor(runnerId: string) {
|
public description: string;
|
||||||
super(`Task runner (${runnerId}) disconnected`);
|
|
||||||
|
constructor(
|
||||||
|
public readonly runnerId: TaskRunner['id'],
|
||||||
|
isCloudDeployment: boolean,
|
||||||
|
) {
|
||||||
|
super('Node execution failed');
|
||||||
|
|
||||||
|
const fixSuggestions = {
|
||||||
|
reduceItems:
|
||||||
|
'Reduce the number of items processed at a time, by batching them using a loop node',
|
||||||
|
increaseMemory:
|
||||||
|
"Increase the memory available to the task runner with 'N8N_RUNNERS_MAX_OLD_SPACE_SIZE' environment variable",
|
||||||
|
upgradePlan: 'Upgrade your cloud plan to increase the available memory',
|
||||||
|
};
|
||||||
|
|
||||||
|
const subtitle =
|
||||||
|
'This can happen for various reasons. Please try executing the node again. If the problem persists, you can try the following:';
|
||||||
|
const suggestions = isCloudDeployment
|
||||||
|
? [fixSuggestions.reduceItems, fixSuggestions.upgradePlan]
|
||||||
|
: [fixSuggestions.reduceItems, fixSuggestions.increaseMemory];
|
||||||
|
const suggestionsText = suggestions
|
||||||
|
.map((suggestion, index) => `${index + 1}. ${suggestion}`)
|
||||||
|
.join('<br/>');
|
||||||
|
|
||||||
|
const description = `${subtitle}<br/><br/>${suggestionsText}`;
|
||||||
|
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { TaskRunnersConfig } from '@n8n/config';
|
import { TaskRunnersConfig } from '@n8n/config';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
|
|
||||||
import { DefaultTaskRunnerDisconnectAnalyzer } from './default-task-runner-disconnect-analyzer';
|
import { DefaultTaskRunnerDisconnectAnalyzer } from './default-task-runner-disconnect-analyzer';
|
||||||
import { TaskRunnerOomError } from './errors/task-runner-oom-error';
|
import { TaskRunnerOomError } from './errors/task-runner-oom-error';
|
||||||
import type { DisconnectErrorOptions } from './runner-types';
|
import type { DisconnectErrorOptions } from './runner-types';
|
||||||
|
@ -16,10 +14,6 @@ import { TaskRunnerProcess } from './task-runner-process';
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
export class InternalTaskRunnerDisconnectAnalyzer extends DefaultTaskRunnerDisconnectAnalyzer {
|
export class InternalTaskRunnerDisconnectAnalyzer extends DefaultTaskRunnerDisconnectAnalyzer {
|
||||||
private get isCloudDeployment() {
|
|
||||||
return config.get('deployment.type') === 'cloud';
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly exitReasonSignal: SlidingWindowSignal<TaskRunnerProcessEventMap, 'exit'>;
|
private readonly exitReasonSignal: SlidingWindowSignal<TaskRunnerProcessEventMap, 'exit'>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -6,6 +6,8 @@ import type { TaskRunner } from './task-broker.service';
|
||||||
import type { AuthlessRequest } from '../requests';
|
import type { AuthlessRequest } from '../requests';
|
||||||
|
|
||||||
export interface DisconnectAnalyzer {
|
export interface DisconnectAnalyzer {
|
||||||
|
isCloudDeployment: boolean;
|
||||||
|
|
||||||
toDisconnectError(opts: DisconnectErrorOptions): Promise<Error>;
|
toDisconnectError(opts: DisconnectErrorOptions): Promise<Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue