diff --git a/BackEnd/.env.example b/BackEnd/.env.example index cc96e2f0..220a6612 100644 --- a/BackEnd/.env.example +++ b/BackEnd/.env.example @@ -30,7 +30,13 @@ FF_ENABLE_NOTIFICATIONS_V2=false # Database DATABASE_URL=postgresql://user:password@localhost:5432/stellar_earn -# Query Logging Configuration +# Connection Pool +DB_POOL_MAX=10 +DB_POOL_MIN=2 +DB_POOL_CONNECTION_TIMEOUT=10000 +DB_POOL_IDLE_TIMEOUT=30000 + +# Query Logging DB_QUERY_LOGGING=true SLOW_QUERY_THRESHOLD=1000 CRITICAL_QUERY_THRESHOLD=5000 diff --git a/BackEnd/src/config/ormconfig.ts b/BackEnd/src/config/ormconfig.ts index bb778061..e6e5a2b6 100644 --- a/BackEnd/src/config/ormconfig.ts +++ b/BackEnd/src/config/ormconfig.ts @@ -60,14 +60,11 @@ export default new DataSource({ logging: process.env.NODE_ENV === 'development', // Add these extra options for Neon extra: { - ssl: { - rejectUnauthorized: false, - require: true, - }, - // Connection pool settings - max: 10, - connectionTimeoutMillis: 10000, - idleTimeoutMillis: 30000, + ssl: { rejectUnauthorized: false, require: true }, + max: parseInt(process.env.DB_POOL_MAX ?? '10', 10), + min: parseInt(process.env.DB_POOL_MIN ?? '2', 10), + connectionTimeoutMillis: parseInt(process.env.DB_POOL_CONNECTION_TIMEOUT ?? '10000', 10), + idleTimeoutMillis: parseInt(process.env.DB_POOL_IDLE_TIMEOUT ?? '30000', 10), }, }); diff --git a/BackEnd/src/database/data-source.ts b/BackEnd/src/database/data-source.ts index a6d74bfc..1d8f5a33 100644 --- a/BackEnd/src/database/data-source.ts +++ b/BackEnd/src/database/data-source.ts @@ -8,16 +8,22 @@ dotenv.config({ path: path.resolve(__dirname, '../../.env') }); class TypeORMQueryLogger implements Logger { private readonly logger = new AppLoggerService(); - private readonly slowQueryThreshold = parseInt(process.env.SLOW_QUERY_THRESHOLD || '1000'); // ms - private readonly enableQueryLogging = process.env.NODE_ENV === 'development' || process.env.DB_QUERY_LOGGING === 'true'; + private readonly slowQueryThreshold = parseInt( + process.env.SLOW_QUERY_THRESHOLD || '1000', + 10 + ); + + private readonly enableQueryLogging = + process.env.NODE_ENV === 'development' || + process.env.DB_QUERY_LOGGING === 'true'; logQuery(query: string, parameters?: any[]): void { if (!this.enableQueryLogging) return; - + this.logger.debug('Database Query', 'Database', { query: query.trim(), parameters, - type: 'query' + type: 'query', }); } @@ -25,7 +31,7 @@ class TypeORMQueryLogger implements Logger { this.logger.error('Database Query Error', error, 'Database', { query: query.trim(), parameters, - type: 'query_error' + type: 'query_error', }); } @@ -35,7 +41,7 @@ class TypeORMQueryLogger implements Logger { parameters, executionTime: time, threshold: this.slowQueryThreshold, - type: 'slow_query' + type: 'slow_query', }); } @@ -65,15 +71,51 @@ class TypeORMQueryLogger implements Logger { export const dataSourceOptions: DataSourceOptions = { type: 'postgres', url: process.env.DATABASE_URL, - entities: [path.join(__dirname, '..', '**', 'entities', '*.entity.{ts,js}')], - migrations: [path.join(__dirname, 'migrations', '*.{ts,js}')], + + // Enable SSL only in production + ssl: + process.env.NODE_ENV === 'production' + ? { rejectUnauthorized: false } + : false, + + entities: [ + path.join(__dirname, '..', '**', 'entities', '*.entity.{ts,js}'), + ], + + migrations: [ + path.join(__dirname, 'migrations', '*.{ts,js}'), + ], + migrationsTableName: 'typeorm_migrations', synchronize: false, - logging: process.env.NODE_ENV === 'development' || process.env.DB_QUERY_LOGGING === 'true', + + logging: + process.env.NODE_ENV === 'development' || + process.env.DB_QUERY_LOGGING === 'true', + logger: new TypeORMQueryLogger(), - maxQueryExecutionTime: parseInt(process.env.SLOW_QUERY_THRESHOLD || '1000'), + + maxQueryExecutionTime: parseInt( + process.env.SLOW_QUERY_THRESHOLD || '1000', + 10 + ), + + extra: { + max: parseInt(process.env.DB_POOL_MAX ?? '10', 10), + min: parseInt(process.env.DB_POOL_MIN ?? '2', 10), + + connectionTimeoutMillis: parseInt( + process.env.DB_POOL_CONNECTION_TIMEOUT ?? '10000', + 10 + ), + + idleTimeoutMillis: parseInt( + process.env.DB_POOL_IDLE_TIMEOUT ?? '30000', + 10 + ), + }, }; const AppDataSource = new DataSource(dataSourceOptions); -export default AppDataSource; +export default AppDataSource; \ No newline at end of file diff --git a/BackEnd/src/modules/health/services/database-pool-monitor.service.ts b/BackEnd/src/modules/health/services/database-pool-monitor.service.ts index 45d749b4..dc274b3d 100644 --- a/BackEnd/src/modules/health/services/database-pool-monitor.service.ts +++ b/BackEnd/src/modules/health/services/database-pool-monitor.service.ts @@ -86,10 +86,10 @@ export class DatabasePoolMonitorService implements OnModuleInit, OnModuleDestroy const extra = options.extra || {}; return { - max: extra.max || 10, - min: extra.min || 0, - connectionTimeoutMillis: extra.connectionTimeoutMillis || 10000, - idleTimeoutMillis: extra.idleTimeoutMillis || 30000, + max: extra.max ?? parseInt(process.env.DB_POOL_MAX ?? '10', 10), + min: extra.min ?? parseInt(process.env.DB_POOL_MIN ?? '2', 10), + connectionTimeoutMillis: extra.connectionTimeoutMillis ?? parseInt(process.env.DB_POOL_CONNECTION_TIMEOUT ?? '10000', 10), + idleTimeoutMillis: extra.idleTimeoutMillis ?? parseInt(process.env.DB_POOL_IDLE_TIMEOUT ?? '30000', 10), }; }