mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-12 15:44:06 -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_MAX_PAYLOAD",
|
||||
"NODE_FUNCTION_ALLOW_BUILTIN",
|
||||
"NODE_FUNCTION_ALLOW_EXTERNAL"
|
||||
"NODE_FUNCTION_ALLOW_EXTERNAL",
|
||||
"NODE_OPTIONS"
|
||||
],
|
||||
"uid": 2000,
|
||||
"gid": 2000
|
||||
|
|
|
@ -42,4 +42,8 @@ export class TaskRunnersConfig {
|
|||
/** Which task runner to launch from the config */
|
||||
@Env('N8N_RUNNERS_LAUNCHER_RUNNER')
|
||||
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,
|
||||
launcherPath: '',
|
||||
launcherRunner: 'javascript',
|
||||
maxOldSpaceSize: '',
|
||||
},
|
||||
sentry: {
|
||||
backendDsn: '',
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('TaskRunnerProcess', () => {
|
|||
runnerConfig.disabled = false;
|
||||
runnerConfig.mode = 'internal_childprocess';
|
||||
const authService = mock<TaskRunnerAuthService>();
|
||||
const taskRunnerProcess = new TaskRunnerProcess(runnerConfig, authService);
|
||||
let taskRunnerProcess = new TaskRunnerProcess(runnerConfig, authService);
|
||||
|
||||
afterEach(async () => {
|
||||
spawnMock.mockClear();
|
||||
|
@ -40,10 +40,31 @@ describe('TaskRunnerProcess', () => {
|
|||
});
|
||||
|
||||
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');
|
||||
process.env.NODE_FUNCTION_ALLOW_BUILTIN = '*';
|
||||
process.env.NODE_FUNCTION_ALLOW_EXTERNAL = '*';
|
||||
runnerConfig.maxOldSpaceSize = '1024';
|
||||
|
||||
await taskRunnerProcess.start();
|
||||
|
||||
|
@ -51,10 +72,20 @@ describe('TaskRunnerProcess', () => {
|
|||
const options = spawnMock.mock.calls[0][2] as SpawnOptions;
|
||||
expect(options.env).toEqual(
|
||||
expect.objectContaining({
|
||||
NODE_FUNCTION_ALLOW_BUILTIN: '*',
|
||||
NODE_FUNCTION_ALLOW_EXTERNAL: '*',
|
||||
NODE_OPTIONS: '--max-old-space-size=1024',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
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 readonly passthroughEnvVars = [
|
||||
'PATH',
|
||||
'NODE_FUNCTION_ALLOW_BUILTIN',
|
||||
'NODE_FUNCTION_ALLOW_EXTERNAL',
|
||||
] as const;
|
||||
|
||||
constructor(
|
||||
private readonly runnerConfig: TaskRunnersConfig,
|
||||
private readonly authService: TaskRunnerAuthService,
|
||||
|
@ -68,26 +74,14 @@ export class TaskRunnerProcess {
|
|||
const startScript = require.resolve('@n8n/task-runner');
|
||||
|
||||
return spawn('node', [startScript], {
|
||||
env: {
|
||||
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,
|
||||
},
|
||||
env: this.getProcessEnvVars(grantToken, n8nUri),
|
||||
});
|
||||
}
|
||||
|
||||
startLauncher(grantToken: string, n8nUri: string) {
|
||||
return spawn(this.runnerConfig.launcherPath, ['launch', this.runnerConfig.launcherRunner], {
|
||||
env: {
|
||||
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,
|
||||
...this.getProcessEnvVars(grantToken, n8nUri),
|
||||
// For debug logging if enabled
|
||||
RUST_LOG: process.env.RUST_LOG,
|
||||
},
|
||||
|
@ -155,4 +149,29 @@ export class TaskRunnerProcess {
|
|||
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