mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
feat(core): Add config to override default database ping interval and default idle connection timeout (#15764)
This commit is contained in:
committed by
GitHub
parent
8201202a54
commit
ac06610485
@@ -84,6 +84,10 @@ class PostgresConfig {
|
||||
@Env('DB_POSTGRESDB_CONNECTION_TIMEOUT')
|
||||
connectionTimeoutMs: number = 20_000;
|
||||
|
||||
/** Postgres idle connection timeout (ms) */
|
||||
@Env('DB_POSTGRESDB_IDLE_CONNECTION_TIMEOUT')
|
||||
idleTimeoutMs: number = 30_000;
|
||||
|
||||
@Nested
|
||||
ssl: PostgresSSLConfig;
|
||||
}
|
||||
@@ -158,6 +162,12 @@ export class DatabaseConfig {
|
||||
@Env('DB_TABLE_PREFIX')
|
||||
tablePrefix: string = '';
|
||||
|
||||
/**
|
||||
* The interval in seconds to ping the database to check if the connection is still alive.
|
||||
*/
|
||||
@Env('DB_PING_INTERVAL_SECONDS')
|
||||
pingIntervalSeconds: number = 2;
|
||||
|
||||
@Nested
|
||||
logging: LoggingConfig;
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ describe('GlobalConfig', () => {
|
||||
rejectUnauthorized: true,
|
||||
},
|
||||
user: 'postgres',
|
||||
idleTimeoutMs: 30_000,
|
||||
},
|
||||
sqlite: {
|
||||
database: 'database.sqlite',
|
||||
@@ -72,6 +73,7 @@ describe('GlobalConfig', () => {
|
||||
tablePrefix: '',
|
||||
type: 'sqlite',
|
||||
isLegacySqlite: true,
|
||||
pingIntervalSeconds: 2,
|
||||
},
|
||||
credentials: {
|
||||
defaultName: 'My credentials',
|
||||
@@ -323,7 +325,9 @@ describe('GlobalConfig', () => {
|
||||
process.env = {
|
||||
DB_POSTGRESDB_HOST: 'some-host',
|
||||
DB_POSTGRESDB_USER: 'n8n',
|
||||
DB_POSTGRESDB_IDLE_CONNECTION_TIMEOUT: '10000',
|
||||
DB_TABLE_PREFIX: 'test_',
|
||||
DB_PING_INTERVAL_SECONDS: '2',
|
||||
NODES_INCLUDE: '["n8n-nodes-base.hackerNews"]',
|
||||
DB_LOGGING_MAX_EXECUTION_TIME: '0',
|
||||
N8N_METRICS: 'TRUE',
|
||||
@@ -339,10 +343,12 @@ describe('GlobalConfig', () => {
|
||||
...defaultConfig.database.postgresdb,
|
||||
host: 'some-host',
|
||||
user: 'n8n',
|
||||
idleTimeoutMs: 10_000,
|
||||
},
|
||||
sqlite: defaultConfig.database.sqlite,
|
||||
tablePrefix: 'test_',
|
||||
type: 'sqlite',
|
||||
pingIntervalSeconds: 2,
|
||||
},
|
||||
endpoints: {
|
||||
...defaultConfig.endpoints,
|
||||
|
||||
@@ -102,6 +102,7 @@ describe('DbConnectionOptions', () => {
|
||||
key: '',
|
||||
rejectUnauthorized: true,
|
||||
},
|
||||
idleTimeoutMs: 30000,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -121,6 +122,9 @@ describe('DbConnectionOptions', () => {
|
||||
migrations: postgresMigrations,
|
||||
connectTimeoutMS: 20000,
|
||||
ssl: false,
|
||||
extra: {
|
||||
idleTimeoutMillis: 30000,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { DatabaseConfig } from '@n8n/config';
|
||||
import type { Migration } from '@n8n/db';
|
||||
import * as migrationHelper from '@n8n/db';
|
||||
import { DataSource, type DataSourceOptions } from '@n8n/typeorm';
|
||||
@@ -17,6 +18,7 @@ describe('DbConnection', () => {
|
||||
let dbConnection: DbConnection;
|
||||
const migrations = [{ name: 'TestMigration1' }, { name: 'TestMigration2' }] as Migration[];
|
||||
const errorReporter = mock<ErrorReporter>();
|
||||
const databaseConfig = mock<DatabaseConfig>();
|
||||
const dataSource = mockDeep<DataSource>({ options: { migrations } });
|
||||
const connectionOptions = mockDeep<DbConnectionOptions>();
|
||||
const postgresOptions: DataSourceOptions = {
|
||||
@@ -35,7 +37,7 @@ describe('DbConnection', () => {
|
||||
connectionOptions.getOptions.mockReturnValue(postgresOptions);
|
||||
(DataSource as jest.Mock) = jest.fn().mockImplementation(() => dataSource);
|
||||
|
||||
dbConnection = new DbConnection(errorReporter, connectionOptions);
|
||||
dbConnection = new DbConnection(errorReporter, connectionOptions, databaseConfig);
|
||||
});
|
||||
|
||||
describe('init', () => {
|
||||
@@ -174,5 +176,29 @@ describe('DbConnection', () => {
|
||||
|
||||
expect(dataSource.query).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should execute ping on schedule', async () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
// ARRANGE
|
||||
dbConnection = new DbConnection(
|
||||
errorReporter,
|
||||
connectionOptions,
|
||||
mock<DatabaseConfig>({
|
||||
pingIntervalSeconds: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
const pingSpy = jest.spyOn(dbConnection as any, 'ping');
|
||||
|
||||
// @ts-expect-error private property
|
||||
dbConnection.scheduleNextPing();
|
||||
jest.advanceTimersByTime(1000);
|
||||
|
||||
expect(pingSpy).toHaveBeenCalled();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,6 +131,9 @@ export class DbConnectionOptions {
|
||||
migrations: postgresMigrations,
|
||||
connectTimeoutMS: postgresConfig.connectionTimeoutMs,
|
||||
ssl,
|
||||
extra: {
|
||||
idleTimeoutMillis: postgresConfig.idleTimeoutMs,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { inTest } from '@n8n/backend-common';
|
||||
import { DatabaseConfig } from '@n8n/config';
|
||||
import type { Migration } from '@n8n/db';
|
||||
import { wrapMigration } from '@n8n/db';
|
||||
import { Memoized } from '@n8n/decorators';
|
||||
@@ -7,6 +8,8 @@ import { DataSource } from '@n8n/typeorm';
|
||||
import { ErrorReporter } from 'n8n-core';
|
||||
import { DbConnectionTimeoutError, ensureError } from 'n8n-workflow';
|
||||
|
||||
import { Time } from '@/constants';
|
||||
|
||||
import { DbConnectionOptions } from './db-connection-options';
|
||||
|
||||
type ConnectionState = {
|
||||
@@ -28,6 +31,7 @@ export class DbConnection {
|
||||
constructor(
|
||||
private readonly errorReporter: ErrorReporter,
|
||||
private readonly connectionOptions: DbConnectionOptions,
|
||||
private readonly databaseConfig: DatabaseConfig,
|
||||
) {
|
||||
this.dataSource = new DataSource(this.options);
|
||||
Container.set(DataSource, this.dataSource);
|
||||
@@ -80,9 +84,12 @@ export class DbConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/** Ping DB connection every 2 seconds */
|
||||
/** Ping DB connection every `pingIntervalSeconds` seconds to check if it is still alive. */
|
||||
private scheduleNextPing() {
|
||||
this.pingTimer = setTimeout(async () => await this.ping(), 2000);
|
||||
this.pingTimer = setTimeout(
|
||||
async () => await this.ping(),
|
||||
this.databaseConfig.pingIntervalSeconds * Time.seconds.toMilliseconds,
|
||||
);
|
||||
}
|
||||
|
||||
private async ping() {
|
||||
|
||||
Reference in New Issue
Block a user