feat(core): Implement task timeouts and heartbeats for runners (no-changelog) (#11690)

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
Iván Ovejero
2024-11-17 15:21:28 +01:00
committed by GitHub
parent 3f9127955a
commit 124ac26e43
18 changed files with 511 additions and 35 deletions

View File

@@ -1,4 +1,16 @@
import { Config, Env } from '@n8n/config';
import { Config, Env, Nested } from '@n8n/config';
@Config
class HealthcheckServerConfig {
@Env('N8N_RUNNERS_SERVER_ENABLED')
enabled: boolean = false;
@Env('N8N_RUNNERS_SERVER_HOST')
host: string = '127.0.0.1';
@Env('N8N_RUNNERS_SERVER_PORT')
port: number = 5680;
}
@Config
export class BaseRunnerConfig {
@@ -13,4 +25,7 @@ export class BaseRunnerConfig {
@Env('N8N_RUNNERS_MAX_CONCURRENCY')
maxConcurrency: number = 5;
@Nested
healthcheckServer!: HealthcheckServerConfig;
}

View File

@@ -0,0 +1,38 @@
import { ApplicationError } from 'n8n-workflow';
import { createServer } from 'node:http';
export class HealthcheckServer {
private server = createServer((_, res) => {
res.writeHead(200);
res.end('OK');
});
async start(host: string, port: number) {
return await new Promise<void>((resolve, reject) => {
const portInUseErrorHandler = (error: NodeJS.ErrnoException) => {
if (error.code === 'EADDRINUSE') {
reject(new ApplicationError(`Port ${port} is already in use`));
} else {
reject(error);
}
};
this.server.on('error', portInUseErrorHandler);
this.server.listen(port, host, () => {
this.server.removeListener('error', portInUseErrorHandler);
console.log(`Healthcheck server listening on ${host}, port ${port}`);
resolve();
});
});
}
async stop() {
return await new Promise<void>((resolve, reject) => {
this.server.close((error) => {
if (error) reject(error);
else resolve();
});
});
}
}

View File

@@ -3,8 +3,10 @@ import Container from 'typedi';
import { MainConfig } from './config/main-config';
import type { ErrorReporter } from './error-reporter';
import type { HealthcheckServer } from './healthcheck-server';
import { JsTaskRunner } from './js-task-runner/js-task-runner';
let healthcheckServer: HealthcheckServer | undefined;
let runner: JsTaskRunner | undefined;
let isShuttingDown = false;
let errorReporter: ErrorReporter | undefined;
@@ -22,6 +24,7 @@ function createSignalHandler(signal: string) {
if (runner) {
await runner.stop();
runner = undefined;
void healthcheckServer?.stop();
}
if (errorReporter) {
@@ -49,6 +52,14 @@ void (async function start() {
runner = new JsTaskRunner(config);
const { enabled, host, port } = config.baseRunnerConfig.healthcheckServer;
if (enabled) {
const { HealthcheckServer } = await import('./healthcheck-server');
healthcheckServer = new HealthcheckServer();
await healthcheckServer.start(host, port);
}
process.on('SIGINT', createSignalHandler('SIGINT'));
process.on('SIGTERM', createSignalHandler('SIGTERM'));
})().catch((e) => {