UNPKG

pms-analysis-reports-mcp-server

Version:

PMS analysis reports server handling maintenance reports, equipment analysis, compliance tracking, and performance metrics with ERP access for data extraction

372 lines 14.3 kB
import { MongoClient } from 'mongodb'; import { config } from './config.js'; import { logger } from './logger.js'; const defaultPoolConfig = { maxPoolSize: config.mongoMaxPoolSize || 20, minPoolSize: config.mongoMinPoolSize || 2, maxIdleTimeMS: 30000, // Close idle connections after 30 seconds serverSelectionTimeoutMS: config.mongoConnectionTimeout || 10000, socketTimeoutMS: config.mongoSocketTimeout || 45000, connectTimeoutMS: config.mongoConnectionTimeout || 10000, retryWrites: true, // Automatically retry failed writes retryReads: true // Automatically retry failed reads }; class MongoConnectionManager { constructor() { this.client = null; this.eltClient = null; this.engineDataClient = null; this.isConnecting = false; this.isEltConnecting = false; this.isEngineDataConnecting = false; this.lastHealthCheck = 0; this.lastEltHealthCheck = 0; this.lastEngineDataHealthCheck = 0; this.healthCheckInterval = 30000; // 30 seconds } createClientOptions(customConfig) { const poolConfig = { ...defaultPoolConfig, ...customConfig }; return { maxPoolSize: poolConfig.maxPoolSize, minPoolSize: poolConfig.minPoolSize, maxIdleTimeMS: poolConfig.maxIdleTimeMS, serverSelectionTimeoutMS: poolConfig.serverSelectionTimeoutMS, socketTimeoutMS: poolConfig.socketTimeoutMS, connectTimeoutMS: poolConfig.connectTimeoutMS, retryWrites: poolConfig.retryWrites, retryReads: poolConfig.retryReads, // Additional production-ready options compressors: ['zlib'], // Enable compression readPreference: 'primaryPreferred', // Prefer primary but allow secondary reads w: 'majority', // Wait for majority acknowledgment journal: true, // Ensure writes are journaled // Connection pool events for monitoring monitorCommands: true }; } async getMongoClient() { // Check if we need to perform health check const now = Date.now(); if (this.client && (now - this.lastHealthCheck) > this.healthCheckInterval) { await this.performHealthCheck(); } if (!this.client) { await this.initializePrimaryConnection(); } return this.client; } async getELTMongoClient() { // Check if we need to perform health check const now = Date.now(); if (this.eltClient && (now - this.lastEltHealthCheck) > this.healthCheckInterval) { await this.performEltHealthCheck(); } if (!this.eltClient) { await this.initializeEltConnection(); } return this.eltClient; } async getEngineDataMongoClient() { // Check if we need to perform health check const now = Date.now(); if (this.engineDataClient && (now - this.lastEngineDataHealthCheck) > this.healthCheckInterval) { await this.performEngineDataHealthCheck(); } if (!this.engineDataClient) { await this.initializeEngineDataConnection(); } return this.engineDataClient; } async initializePrimaryConnection() { if (this.isConnecting) { // Wait for ongoing connection attempt while (this.isConnecting && !this.client) { await new Promise(resolve => setTimeout(resolve, 100)); } return; } this.isConnecting = true; try { logger.info('Initializing primary MongoDB connection pool'); this.client = new MongoClient(config.mongoUri, this.createClientOptions()); // Set up connection event handlers this.setupConnectionEventHandlers(this.client, 'primary'); await this.client.connect(); // Verify connection with ping await this.client.db().admin().ping(); this.lastHealthCheck = Date.now(); logger.info('Primary MongoDB connection pool initialized successfully', { maxPoolSize: defaultPoolConfig.maxPoolSize, minPoolSize: defaultPoolConfig.minPoolSize }); } catch (error) { logger.error('Failed to initialize primary MongoDB connection', { error }); this.client = null; throw error; } finally { this.isConnecting = false; } } async initializeEltConnection() { if (this.isEltConnecting) { // Wait for ongoing connection attempt while (this.isEltConnecting && !this.eltClient) { await new Promise(resolve => setTimeout(resolve, 100)); } return; } this.isEltConnecting = true; try { logger.info('Initializing ELT MongoDB connection pool'); this.eltClient = new MongoClient(config.secondaryDbUri, this.createClientOptions()); // Set up connection event handlers this.setupConnectionEventHandlers(this.eltClient, 'elt'); await this.eltClient.connect(); // Verify connection with ping await this.eltClient.db().admin().ping(); this.lastEltHealthCheck = Date.now(); logger.info('ELT MongoDB connection pool initialized successfully', { maxPoolSize: defaultPoolConfig.maxPoolSize, minPoolSize: defaultPoolConfig.minPoolSize }); } catch (error) { logger.error('Failed to initialize ELT MongoDB connection', { error }); this.eltClient = null; throw error; } finally { this.isEltConnecting = false; } } async initializeEngineDataConnection() { if (this.isEngineDataConnecting) { // Wait for ongoing connection attempt while (this.isEngineDataConnecting && !this.engineDataClient) { await new Promise(resolve => setTimeout(resolve, 100)); } return; } this.isEngineDataConnecting = true; try { logger.info('Initializing Engine Data MongoDB connection pool'); this.engineDataClient = new MongoClient(config.thirdDbUri, this.createClientOptions()); // Set up connection event handlers this.setupConnectionEventHandlers(this.engineDataClient, 'engine-data'); await this.engineDataClient.connect(); // Verify connection with ping await this.engineDataClient.db().admin().ping(); this.lastEngineDataHealthCheck = Date.now(); logger.info('Engine Data MongoDB connection pool initialized successfully', { maxPoolSize: defaultPoolConfig.maxPoolSize, minPoolSize: defaultPoolConfig.minPoolSize }); } catch (error) { logger.error('Failed to initialize Engine Data MongoDB connection', { error }); this.engineDataClient = null; throw error; } finally { this.isEngineDataConnecting = false; } } setupConnectionEventHandlers(client, connectionType) { const prefix = connectionType === 'primary' ? 'Primary' : connectionType === 'elt' ? 'ELT' : 'Engine Data'; client.on('connectionPoolCreated', (event) => { logger.info(`${prefix} MongoDB connection pool created`, { address: event.address, maxPoolSize: event.options?.maxPoolSize }); }); client.on('connectionPoolClosed', (event) => { logger.warn(`${prefix} MongoDB connection pool closed`, { address: event.address }); }); client.on('connectionCreated', (event) => { logger.debug(`${prefix} MongoDB connection created`, { connectionId: event.connectionId, address: event.address }); }); client.on('connectionClosed', (event) => { logger.debug(`${prefix} MongoDB connection closed`, { connectionId: event.connectionId, address: event.address, reason: event.reason }); }); client.on('commandStarted', (event) => { logger.debug(`${prefix} MongoDB command started`, { commandName: event.commandName, databaseName: event.databaseName, requestId: event.requestId }); }); client.on('commandFailed', (event) => { logger.error(`${prefix} MongoDB command failed`, { commandName: event.commandName, failure: event.failure, requestId: event.requestId }); }); } async performHealthCheck() { try { if (this.client) { await this.client.db().admin().ping(); this.lastHealthCheck = Date.now(); logger.debug('Primary MongoDB health check passed'); } } catch (error) { logger.error('Primary MongoDB health check failed', { error }); // Reset client to force reconnection on next request await this.closePrimaryConnection(); } } async performEltHealthCheck() { try { if (this.eltClient) { await this.eltClient.db().admin().ping(); this.lastEltHealthCheck = Date.now(); logger.debug('ELT MongoDB health check passed'); } } catch (error) { logger.error('ELT MongoDB health check failed', { error }); // Reset client to force reconnection on next request await this.closeEltConnection(); } } async performEngineDataHealthCheck() { try { if (this.engineDataClient) { await this.engineDataClient.db().admin().ping(); this.lastEngineDataHealthCheck = Date.now(); logger.debug('Engine Data MongoDB health check passed'); } } catch (error) { logger.error('Engine Data MongoDB health check failed', { error }); // Reset client to force reconnection on next request await this.closeEngineDataConnection(); } } async closePrimaryConnection() { if (this.client) { try { await this.client.close(); logger.info('Primary MongoDB connection closed'); } catch (error) { logger.error('Error closing primary MongoDB connection', { error }); } finally { this.client = null; this.lastHealthCheck = 0; } } } async closeEltConnection() { if (this.eltClient) { try { await this.eltClient.close(); logger.info('ELT MongoDB connection closed'); } catch (error) { logger.error('Error closing ELT MongoDB connection', { error }); } finally { this.eltClient = null; this.lastEltHealthCheck = 0; } } } async closeEngineDataConnection() { if (this.engineDataClient) { try { await this.engineDataClient.close(); logger.info('Engine Data MongoDB connection closed'); } catch (error) { logger.error('Error closing Engine Data MongoDB connection', { error }); } finally { this.engineDataClient = null; this.lastEngineDataHealthCheck = 0; } } } async closeAllConnections() { await Promise.all([ this.closePrimaryConnection(), this.closeEltConnection(), this.closeEngineDataConnection() ]); } getConnectionStats() { return { primary: { connected: !!this.client, lastHealthCheck: this.lastHealthCheck, isConnecting: this.isConnecting }, elt: { connected: !!this.eltClient, lastHealthCheck: this.lastEltHealthCheck, isConnecting: this.isEltConnecting }, engineData: { connected: !!this.engineDataClient, lastHealthCheck: this.lastEngineDataHealthCheck, isConnecting: this.isEngineDataConnecting } }; } } // Create singleton instance const connectionManager = new MongoConnectionManager(); // Export the improved connection functions export async function getMongoClient() { return connectionManager.getMongoClient(); } export async function getELTMongoClient() { return connectionManager.getELTMongoClient(); } export async function getEngineDataMongoClient() { return connectionManager.getEngineDataMongoClient(); } export async function getDatabase() { const client = await getMongoClient(); return client.db(config.dbName); } export async function getELTDatabase() { const client = await getELTMongoClient(); return client.db(config.secondaryDbName); } export async function getEngineDataDatabase() { const client = await getEngineDataMongoClient(); return client.db(config.thirdDbName); } // Export additional utility functions export async function closeAllConnections() { return connectionManager.closeAllConnections(); } export function getConnectionStats() { return connectionManager.getConnectionStats(); } // Handle graceful shutdown process.on('SIGINT', async () => { logger.info('Received SIGINT, closing MongoDB connections gracefully'); await closeAllConnections(); process.exit(0); }); process.on('SIGTERM', async () => { logger.info('Received SIGTERM, closing MongoDB connections gracefully'); await closeAllConnections(); process.exit(0); }); //# sourceMappingURL=mongodb.js.map