mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 04:47:29 -08:00
feat: N8N_RUNNERS_MAX_OLD_SPACE_SIZE configuration (no-changelog) (#11419)
This commit is contained in:
parent
3f30a08c8a
commit
c56f30ce15
|
@ -11,7 +11,8 @@
|
||||||
"N8N_RUNNERS_N8N_URI",
|
"N8N_RUNNERS_N8N_URI",
|
||||||
"N8N_RUNNERS_MAX_PAYLOAD",
|
"N8N_RUNNERS_MAX_PAYLOAD",
|
||||||
"NODE_FUNCTION_ALLOW_BUILTIN",
|
"NODE_FUNCTION_ALLOW_BUILTIN",
|
||||||
"NODE_FUNCTION_ALLOW_EXTERNAL"
|
"NODE_FUNCTION_ALLOW_EXTERNAL",
|
||||||
|
"NODE_OPTIONS"
|
||||||
],
|
],
|
||||||
"uid": 2000,
|
"uid": 2000,
|
||||||
"gid": 2000
|
"gid": 2000
|
||||||
|
|
|
@ -42,4 +42,8 @@ export class TaskRunnersConfig {
|
||||||
/** Which task runner to launch from the config */
|
/** Which task runner to launch from the config */
|
||||||
@Env('N8N_RUNNERS_LAUNCHER_RUNNER')
|
@Env('N8N_RUNNERS_LAUNCHER_RUNNER')
|
||||||
launcherRunner: string = 'javascript';
|
launcherRunner: string = 'javascript';
|
||||||
|
|
||||||
|
/** The --max-old-space-size option to use for the runner (in MB). Default means node.js will determine it based on the available memory. */
|
||||||
|
@Env('N8N_RUNNERS_MAX_OLD_SPACE_SIZE')
|
||||||
|
maxOldSpaceSize: string = '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,7 @@ describe('GlobalConfig', () => {
|
||||||
port: 5679,
|
port: 5679,
|
||||||
launcherPath: '',
|
launcherPath: '',
|
||||||
launcherRunner: 'javascript',
|
launcherRunner: 'javascript',
|
||||||
|
maxOldSpaceSize: '',
|
||||||
},
|
},
|
||||||
sentry: {
|
sentry: {
|
||||||
backendDsn: '',
|
backendDsn: '',
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe('TaskRunnerProcess', () => {
|
||||||
runnerConfig.disabled = false;
|
runnerConfig.disabled = false;
|
||||||
runnerConfig.mode = 'internal_childprocess';
|
runnerConfig.mode = 'internal_childprocess';
|
||||||
const authService = mock<TaskRunnerAuthService>();
|
const authService = mock<TaskRunnerAuthService>();
|
||||||
const taskRunnerProcess = new TaskRunnerProcess(runnerConfig, authService);
|
let taskRunnerProcess = new TaskRunnerProcess(runnerConfig, authService);
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
spawnMock.mockClear();
|
spawnMock.mockClear();
|
||||||
|
@ -40,10 +40,31 @@ describe('TaskRunnerProcess', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('start', () => {
|
describe('start', () => {
|
||||||
it('should propagate NODE_FUNCTION_ALLOW_BUILTIN and NODE_FUNCTION_ALLOW_EXTERNAL from env', async () => {
|
beforeEach(() => {
|
||||||
|
taskRunnerProcess = new TaskRunnerProcess(runnerConfig, authService);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(['PATH', 'NODE_FUNCTION_ALLOW_BUILTIN', 'NODE_FUNCTION_ALLOW_EXTERNAL'])(
|
||||||
|
'should propagate %s from env as is',
|
||||||
|
async (envVar) => {
|
||||||
|
jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
|
||||||
|
process.env[envVar] = 'custom value';
|
||||||
|
|
||||||
|
await taskRunnerProcess.start();
|
||||||
|
|
||||||
|
// @ts-expect-error The type is not correct
|
||||||
|
const options = spawnMock.mock.calls[0][2] as SpawnOptions;
|
||||||
|
expect(options.env).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
[envVar]: 'custom value',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should pass NODE_OPTIONS env if maxOldSpaceSize is configured', async () => {
|
||||||
jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
|
jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
|
||||||
process.env.NODE_FUNCTION_ALLOW_BUILTIN = '*';
|
runnerConfig.maxOldSpaceSize = '1024';
|
||||||
process.env.NODE_FUNCTION_ALLOW_EXTERNAL = '*';
|
|
||||||
|
|
||||||
await taskRunnerProcess.start();
|
await taskRunnerProcess.start();
|
||||||
|
|
||||||
|
@ -51,10 +72,20 @@ describe('TaskRunnerProcess', () => {
|
||||||
const options = spawnMock.mock.calls[0][2] as SpawnOptions;
|
const options = spawnMock.mock.calls[0][2] as SpawnOptions;
|
||||||
expect(options.env).toEqual(
|
expect(options.env).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
NODE_FUNCTION_ALLOW_BUILTIN: '*',
|
NODE_OPTIONS: '--max-old-space-size=1024',
|
||||||
NODE_FUNCTION_ALLOW_EXTERNAL: '*',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not pass NODE_OPTIONS env if maxOldSpaceSize is not configured', async () => {
|
||||||
|
jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken');
|
||||||
|
runnerConfig.maxOldSpaceSize = '';
|
||||||
|
|
||||||
|
await taskRunnerProcess.start();
|
||||||
|
|
||||||
|
// @ts-expect-error The type is not correct
|
||||||
|
const options = spawnMock.mock.calls[0][2] as SpawnOptions;
|
||||||
|
expect(options.env).not.toHaveProperty('NODE_OPTIONS');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,6 +38,12 @@ export class TaskRunnerProcess {
|
||||||
|
|
||||||
private isShuttingDown = false;
|
private isShuttingDown = false;
|
||||||
|
|
||||||
|
private readonly passthroughEnvVars = [
|
||||||
|
'PATH',
|
||||||
|
'NODE_FUNCTION_ALLOW_BUILTIN',
|
||||||
|
'NODE_FUNCTION_ALLOW_EXTERNAL',
|
||||||
|
] as const;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly runnerConfig: TaskRunnersConfig,
|
private readonly runnerConfig: TaskRunnersConfig,
|
||||||
private readonly authService: TaskRunnerAuthService,
|
private readonly authService: TaskRunnerAuthService,
|
||||||
|
@ -68,26 +74,14 @@ export class TaskRunnerProcess {
|
||||||
const startScript = require.resolve('@n8n/task-runner');
|
const startScript = require.resolve('@n8n/task-runner');
|
||||||
|
|
||||||
return spawn('node', [startScript], {
|
return spawn('node', [startScript], {
|
||||||
env: {
|
env: this.getProcessEnvVars(grantToken, n8nUri),
|
||||||
PATH: process.env.PATH,
|
|
||||||
N8N_RUNNERS_GRANT_TOKEN: grantToken,
|
|
||||||
N8N_RUNNERS_N8N_URI: n8nUri,
|
|
||||||
N8N_RUNNERS_MAX_PAYLOAD: this.runnerConfig.maxPayload.toString(),
|
|
||||||
NODE_FUNCTION_ALLOW_BUILTIN: process.env.NODE_FUNCTION_ALLOW_BUILTIN,
|
|
||||||
NODE_FUNCTION_ALLOW_EXTERNAL: process.env.NODE_FUNCTION_ALLOW_EXTERNAL,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startLauncher(grantToken: string, n8nUri: string) {
|
startLauncher(grantToken: string, n8nUri: string) {
|
||||||
return spawn(this.runnerConfig.launcherPath, ['launch', this.runnerConfig.launcherRunner], {
|
return spawn(this.runnerConfig.launcherPath, ['launch', this.runnerConfig.launcherRunner], {
|
||||||
env: {
|
env: {
|
||||||
PATH: process.env.PATH,
|
...this.getProcessEnvVars(grantToken, n8nUri),
|
||||||
N8N_RUNNERS_GRANT_TOKEN: grantToken,
|
|
||||||
N8N_RUNNERS_N8N_URI: n8nUri,
|
|
||||||
N8N_RUNNERS_MAX_PAYLOAD: this.runnerConfig.maxPayload.toString(),
|
|
||||||
NODE_FUNCTION_ALLOW_BUILTIN: process.env.NODE_FUNCTION_ALLOW_BUILTIN,
|
|
||||||
NODE_FUNCTION_ALLOW_EXTERNAL: process.env.NODE_FUNCTION_ALLOW_EXTERNAL,
|
|
||||||
// For debug logging if enabled
|
// For debug logging if enabled
|
||||||
RUST_LOG: process.env.RUST_LOG,
|
RUST_LOG: process.env.RUST_LOG,
|
||||||
},
|
},
|
||||||
|
@ -155,4 +149,29 @@ export class TaskRunnerProcess {
|
||||||
setImmediate(async () => await this.start());
|
setImmediate(async () => await this.start());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getProcessEnvVars(grantToken: string, n8nUri: string) {
|
||||||
|
const envVars: Record<string, string> = {
|
||||||
|
N8N_RUNNERS_GRANT_TOKEN: grantToken,
|
||||||
|
N8N_RUNNERS_N8N_URI: n8nUri,
|
||||||
|
N8N_RUNNERS_MAX_PAYLOAD: this.runnerConfig.maxPayload.toString(),
|
||||||
|
...this.getPassthroughEnvVars(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.runnerConfig.maxOldSpaceSize) {
|
||||||
|
envVars.NODE_OPTIONS = `--max-old-space-size=${this.runnerConfig.maxOldSpaceSize}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return envVars;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPassthroughEnvVars() {
|
||||||
|
return this.passthroughEnvVars.reduce<Record<string, string>>((env, key) => {
|
||||||
|
if (process.env[key]) {
|
||||||
|
env[key] = process.env[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue