feat(docker,cache,proxy): improve container runtime defaults and add configurable connection limits
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '11.10.7',
|
||||
version: '11.11.0',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
26
ts/cache/classes.cachedb.ts
vendored
26
ts/cache/classes.cachedb.ts
vendored
@@ -15,15 +15,15 @@ export interface ICacheDbOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* CacheDb - Wrapper around LocalTsmDb and smartdata
|
||||
* CacheDb - Wrapper around LocalSmartDb and smartdata
|
||||
*
|
||||
* Provides persistent caching using smartdata as the ORM layer
|
||||
* and LocalTsmDb as the embedded database engine.
|
||||
* and LocalSmartDb as the embedded database engine.
|
||||
*/
|
||||
export class CacheDb {
|
||||
private static instance: CacheDb | null = null;
|
||||
|
||||
private localTsmDb!: plugins.smartmongo.LocalTsmDb;
|
||||
private localSmartDb!: plugins.smartdb.LocalSmartDb;
|
||||
private smartdataDb!: plugins.smartdata.SmartdataDb;
|
||||
private options: Required<ICacheDbOptions>;
|
||||
private isStarted: boolean = false;
|
||||
@@ -55,8 +55,8 @@ export class CacheDb {
|
||||
|
||||
/**
|
||||
* Start the cache database
|
||||
* - Initializes LocalTsmDb with file persistence
|
||||
* - Connects smartdata to the LocalTsmDb via Unix socket
|
||||
* - Initializes LocalSmartDb with file persistence
|
||||
* - Connects smartdata to the LocalSmartDb via Unix socket
|
||||
*/
|
||||
public async start(): Promise<void> {
|
||||
if (this.isStarted) {
|
||||
@@ -68,16 +68,16 @@ export class CacheDb {
|
||||
// Ensure storage directory exists
|
||||
await plugins.fsUtils.ensureDir(this.options.storagePath);
|
||||
|
||||
// Create LocalTsmDb instance
|
||||
this.localTsmDb = new plugins.smartmongo.LocalTsmDb({
|
||||
// Create LocalSmartDb instance
|
||||
this.localSmartDb = new plugins.smartdb.LocalSmartDb({
|
||||
folderPath: this.options.storagePath,
|
||||
});
|
||||
|
||||
// Start LocalTsmDb and get connection info
|
||||
const connectionInfo = await this.localTsmDb.start();
|
||||
// Start LocalSmartDb and get connection info
|
||||
const connectionInfo = await this.localSmartDb.start();
|
||||
|
||||
if (this.options.debug) {
|
||||
logger.log('debug', `LocalTsmDb started with URI: ${connectionInfo.connectionUri}`);
|
||||
logger.log('debug', `LocalSmartDb started with URI: ${connectionInfo.connectionUri}`);
|
||||
}
|
||||
|
||||
// Initialize smartdata with the connection URI
|
||||
@@ -109,9 +109,9 @@ export class CacheDb {
|
||||
await this.smartdataDb.close();
|
||||
}
|
||||
|
||||
// Stop LocalTsmDb
|
||||
if (this.localTsmDb) {
|
||||
await this.localTsmDb.stop();
|
||||
// Stop LocalSmartDb
|
||||
if (this.localSmartDb) {
|
||||
await this.localSmartDb.stop();
|
||||
}
|
||||
|
||||
this.isStarted = false;
|
||||
|
||||
@@ -528,10 +528,36 @@ export class DcRouter {
|
||||
}
|
||||
|
||||
public async start() {
|
||||
await this.checkSystemLimits();
|
||||
logger.log('info', 'Starting DcRouter Services');
|
||||
await this.serviceManager.start();
|
||||
this.logStartupSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect OS-level resource limits and warn if they are too low for production use.
|
||||
* This is detection only — no attempts to raise limits.
|
||||
*/
|
||||
private async checkSystemLimits(): Promise<void> {
|
||||
try {
|
||||
const fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
||||
const limitsContent = await fs.file('/proc/self/limits').encoding('utf8').read() as string;
|
||||
const nofileLine = limitsContent.split('\n').find((line: string) => line.startsWith('Max open files'));
|
||||
if (nofileLine) {
|
||||
const parts = nofileLine.split(/\s{2,}/);
|
||||
const softLimit = parseInt(parts[1], 10);
|
||||
const hardLimit = parseInt(parts[2], 10);
|
||||
if (softLimit < 65536) {
|
||||
logger.log('warn', `File descriptor soft limit is ${softLimit} (hard: ${hardLimit}). ` +
|
||||
`For production use, set --ulimit nofile=65536:65536 on the container runtime.`);
|
||||
} else {
|
||||
logger.log('info', `File descriptor limits: soft=${softLimit}, hard=${hardLimit}`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Non-Linux or /proc not available — silently skip
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log comprehensive startup summary
|
||||
@@ -708,9 +734,28 @@ export class DcRouter {
|
||||
// Track cert entries loaded from cert store so we can populate certificateStatusMap after start
|
||||
const loadedCertEntries: Array<{domain: string; publicKey: string; validUntil?: number; validFrom?: number}> = [];
|
||||
|
||||
// Create SmartProxy configuration
|
||||
// Create SmartProxy configuration with sensible gateway defaults.
|
||||
// User's smartProxyConfig overrides these defaults via spread.
|
||||
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
|
||||
// --- dcrouter gateway defaults ---
|
||||
maxConnectionsPerIP: 100,
|
||||
connectionRateLimitPerMinute: 600,
|
||||
socketTimeout: 120_000,
|
||||
inactivityTimeout: 120_000,
|
||||
keepAlive: true,
|
||||
noDelay: true,
|
||||
gracefulShutdownTimeout: 30_000,
|
||||
// --- user overrides ---
|
||||
...this.options.smartProxyConfig,
|
||||
// --- deep-merge defaults.security so user can override maxConnections ---
|
||||
defaults: {
|
||||
...this.options.smartProxyConfig?.defaults,
|
||||
security: {
|
||||
maxConnections: 50_000,
|
||||
...this.options.smartProxyConfig?.defaults?.security,
|
||||
},
|
||||
},
|
||||
// --- always set by dcrouter (after spread) ---
|
||||
routes,
|
||||
acme: acmeConfig,
|
||||
certStore: {
|
||||
|
||||
@@ -47,13 +47,13 @@ import * as qenv from '@push.rocks/qenv';
|
||||
import * as smartacme from '@push.rocks/smartacme';
|
||||
import * as smartdata from '@push.rocks/smartdata';
|
||||
import * as smartdns from '@push.rocks/smartdns';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartfs from '@push.rocks/smartfs';
|
||||
import * as smartguard from '@push.rocks/smartguard';
|
||||
import * as smartjwt from '@push.rocks/smartjwt';
|
||||
import * as smartlog from '@push.rocks/smartlog';
|
||||
import * as smartmetrics from '@push.rocks/smartmetrics';
|
||||
import * as smartmta from '@push.rocks/smartmta';
|
||||
import * as smartmongo from '@push.rocks/smartmongo';
|
||||
import * as smartdb from '@push.rocks/smartdb';
|
||||
import * as smartnetwork from '@push.rocks/smartnetwork';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
import * as smartproxy from '@push.rocks/smartproxy';
|
||||
@@ -64,7 +64,7 @@ import * as smartrx from '@push.rocks/smartrx';
|
||||
import * as smartunique from '@push.rocks/smartunique';
|
||||
import * as taskbuffer from '@push.rocks/taskbuffer';
|
||||
|
||||
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmetrics, smartmongo, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
|
||||
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfs, smartguard, smartjwt, smartlog, smartmetrics, smartdb, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
|
||||
|
||||
// Define SmartLog types for use in error handling
|
||||
export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';
|
||||
@@ -90,7 +90,7 @@ export {
|
||||
uuid,
|
||||
}
|
||||
|
||||
// Filesystem utilities (compatibility helpers for smartfile v13+)
|
||||
// Filesystem utilities
|
||||
export const fsUtils = {
|
||||
/**
|
||||
* Ensure a directory exists, creating it recursively if needed (sync)
|
||||
|
||||
Reference in New Issue
Block a user