@nimee/initialize-ms 1.0.74 → 1.0.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +83 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/mongooseConnectionV2.d.ts +87 -0
- package/dist/src/mongooseConnectionV2.js +292 -0
- package/dist/src/mongooseConnectionV2.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +65 -3
- package/src/mongooseConnectionV2.ts +338 -0
- package/dist/src/tracer.d.ts +0 -12
- package/dist/src/tracer.js +0 -55
- package/dist/src/tracer.js.map +0 -1
package/dist/src/index.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ declare class AppInitializer {
|
|
|
5
5
|
server: any;
|
|
6
6
|
private serviceName;
|
|
7
7
|
initialize(server: any, serverType: string, serviceName: string, entryPointsInitializer: any, connectionString: string, dbConnection: any, blackFeatureList: Array<string>, port: number): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Setup periodic health status logging (optional)
|
|
10
|
+
* Only active when LOG_HEALTH_STATUS=true and using V2 connection
|
|
11
|
+
*/
|
|
12
|
+
private setupPeriodicHealthLogging;
|
|
8
13
|
}
|
|
9
14
|
export default AppInitializer;
|
|
10
15
|
export { TracerCoralogix };
|
|
16
|
+
export { default as mongooseConnectionV2 } from "./mongooseConnectionV2";
|
package/dist/src/index.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -12,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
36
|
};
|
|
14
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.TracerCoralogix = void 0;
|
|
38
|
+
exports.mongooseConnectionV2 = exports.TracerCoralogix = void 0;
|
|
16
39
|
const tracerCoralogix_1 = __importDefault(require("./tracerCoralogix"));
|
|
17
40
|
exports.TracerCoralogix = tracerCoralogix_1.default;
|
|
18
41
|
if (process.env.NODE_ENV !== "local" && process.env.NODE_ENV !== "dev") {
|
|
@@ -44,8 +67,9 @@ if (process.env.NODE_ENV !== "local" && process.env.NODE_ENV !== "dev") {
|
|
|
44
67
|
const fastify_1 = __importDefault(require("./fastify"));
|
|
45
68
|
const logger_1 = __importDefault(require("@nimee/logger"));
|
|
46
69
|
const error_handler_1 = require("@nimee/error-handler");
|
|
47
|
-
const health_1 =
|
|
70
|
+
const health_1 = __importStar(require("@nimee/health"));
|
|
48
71
|
const metrics_1 = require("@nimee/metrics");
|
|
72
|
+
const mongooseConnectionV2_1 = __importDefault(require("./mongooseConnectionV2"));
|
|
49
73
|
class AppInitializer {
|
|
50
74
|
initialize(server, serverType, serviceName, entryPointsInitializer, connectionString, dbConnection, blackFeatureList, port) {
|
|
51
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -69,8 +93,31 @@ class AppInitializer {
|
|
|
69
93
|
// await redisService.initRedisConnection(this.serviceName, `${this.serviceName}_`);
|
|
70
94
|
}
|
|
71
95
|
error_handler_1.errorHandler.registerAndHandleAllErrors(this.server);
|
|
72
|
-
|
|
73
|
-
|
|
96
|
+
// Store httpServer reference for HealthServiceV2
|
|
97
|
+
this.httpServer = this.server.server;
|
|
98
|
+
// Choose health service version based on blacklist
|
|
99
|
+
if (blackFeatureList.includes("healthV1")) {
|
|
100
|
+
// Use enhanced HealthServiceV2 with MongoDB health checks
|
|
101
|
+
logger_1.default.info(`[${this.serviceName}] Setting up HealthServiceV2 with MongoDB monitoring`);
|
|
102
|
+
const healthServiceV2 = new health_1.HealthServiceV2(this.server, this.httpServer, {
|
|
103
|
+
livenessPath: "/liveness",
|
|
104
|
+
readinessPath: "/readiness",
|
|
105
|
+
healthPath: "/health",
|
|
106
|
+
includeDbCheck: true,
|
|
107
|
+
dbTimeout: 5000
|
|
108
|
+
});
|
|
109
|
+
healthServiceV2.start(this.serviceName);
|
|
110
|
+
// Use defaultMetricsExporter for consistent Prometheus metrics
|
|
111
|
+
metrics_1.defaultMetricsExporter.collectDefaultMetricsAndExport(this.server, "/metrics", 2000, process.env.logLevel);
|
|
112
|
+
// Setup periodic health logging if enabled
|
|
113
|
+
this.setupPeriodicHealthLogging();
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Use default HealthService V1 (backward compatible)
|
|
117
|
+
logger_1.default.info(`[${this.serviceName}] Setting up default HealthService V1`);
|
|
118
|
+
yield new health_1.default(this.server, undefined).start(this.serviceName);
|
|
119
|
+
metrics_1.defaultMetricsExporter.collectDefaultMetricsAndExport(this.server, "/metrics", 2000, process.env.logLevel);
|
|
120
|
+
}
|
|
74
121
|
if (entryPointsInitializer) {
|
|
75
122
|
const allEntryPointInitializers = [entryPointsInitializer];
|
|
76
123
|
allEntryPointInitializers.forEach((entryPointInitializer) => {
|
|
@@ -83,6 +130,38 @@ class AppInitializer {
|
|
|
83
130
|
yield fastify.listen(port);
|
|
84
131
|
});
|
|
85
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Setup periodic health status logging (optional)
|
|
135
|
+
* Only active when LOG_HEALTH_STATUS=true and using V2 connection
|
|
136
|
+
*/
|
|
137
|
+
setupPeriodicHealthLogging() {
|
|
138
|
+
if (process.env.LOG_HEALTH_STATUS === "true") {
|
|
139
|
+
logger_1.default.info(`[${this.serviceName}] Enabling periodic health status logging (every 60s)`);
|
|
140
|
+
setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
try {
|
|
142
|
+
const isHealthy = yield mongooseConnectionV2_1.default.isHealthy();
|
|
143
|
+
const state = mongooseConnectionV2_1.default.getConnectionState();
|
|
144
|
+
// Always log health status for monitoring
|
|
145
|
+
logger_1.default.info(`[${this.serviceName}] Health check result: ${JSON.stringify({
|
|
146
|
+
healthy: isHealthy,
|
|
147
|
+
readyState: state.readyState,
|
|
148
|
+
readyStateText: state.readyStateText,
|
|
149
|
+
retryCount: state.retryCount,
|
|
150
|
+
timestamp: new Date().toISOString()
|
|
151
|
+
})}`);
|
|
152
|
+
// Still warn if unhealthy
|
|
153
|
+
if (!isHealthy || state.readyState !== 1) {
|
|
154
|
+
logger_1.default.warn(`[${this.serviceName}] MongoDB is unhealthy!`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger_1.default.error(`[${this.serviceName}] Health check error: ${JSON.stringify(error)}`);
|
|
159
|
+
}
|
|
160
|
+
}), 60000); // Check every minute
|
|
161
|
+
}
|
|
162
|
+
}
|
|
86
163
|
}
|
|
87
164
|
exports.default = AppInitializer;
|
|
165
|
+
var mongooseConnectionV2_2 = require("./mongooseConnectionV2");
|
|
166
|
+
Object.defineProperty(exports, "mongooseConnectionV2", { enumerable: true, get: function () { return __importDefault(mongooseConnectionV2_2).default; } });
|
|
88
167
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/// <reference types="mongoose/types/aggregate" />
|
|
2
|
+
/// <reference types="mongoose/types/callback" />
|
|
3
|
+
/// <reference types="mongoose/types/collection" />
|
|
4
|
+
/// <reference types="mongoose/types/connection" />
|
|
5
|
+
/// <reference types="mongoose/types/cursor" />
|
|
6
|
+
/// <reference types="mongoose/types/document" />
|
|
7
|
+
/// <reference types="mongoose/types/error" />
|
|
8
|
+
/// <reference types="mongoose/types/expressions" />
|
|
9
|
+
/// <reference types="mongoose/types/helpers" />
|
|
10
|
+
/// <reference types="mongoose/types/middlewares" />
|
|
11
|
+
/// <reference types="mongoose/types/indexes" />
|
|
12
|
+
/// <reference types="mongoose/types/models" />
|
|
13
|
+
/// <reference types="mongoose/types/mongooseoptions" />
|
|
14
|
+
/// <reference types="mongoose/types/pipelinestage" />
|
|
15
|
+
/// <reference types="mongoose/types/populate" />
|
|
16
|
+
/// <reference types="mongoose/types/query" />
|
|
17
|
+
/// <reference types="mongoose/types/schemaoptions" />
|
|
18
|
+
/// <reference types="mongoose/types/session" />
|
|
19
|
+
/// <reference types="mongoose/types/types" />
|
|
20
|
+
/// <reference types="mongoose/types/utility" />
|
|
21
|
+
/// <reference types="mongoose/types/validation" />
|
|
22
|
+
/// <reference types="mongoose/types/virtuals" />
|
|
23
|
+
/// <reference types="mongoose/types/schematypes" />
|
|
24
|
+
/// <reference types="mongoose/types/inferschematype" />
|
|
25
|
+
/// <reference types="mongoose/types/inferrawdoctype" />
|
|
26
|
+
import { Connection } from "mongoose";
|
|
27
|
+
interface ConnectionConfig {
|
|
28
|
+
maxRetries?: number;
|
|
29
|
+
retryDelay?: number;
|
|
30
|
+
healthCheckInterval?: number;
|
|
31
|
+
enableHealthCheck?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare class MongooseConnection {
|
|
34
|
+
private db;
|
|
35
|
+
private connectionString;
|
|
36
|
+
private serviceName;
|
|
37
|
+
private isConnecting;
|
|
38
|
+
private retryCount;
|
|
39
|
+
private healthCheckInterval;
|
|
40
|
+
private config;
|
|
41
|
+
static getDefaultInstance(): MongooseConnection;
|
|
42
|
+
/**
|
|
43
|
+
* Get production-ready Mongoose connection options
|
|
44
|
+
*/
|
|
45
|
+
private getConnectionOptions;
|
|
46
|
+
/**
|
|
47
|
+
* Setup connection event handlers for monitoring and recovery
|
|
48
|
+
*/
|
|
49
|
+
private setupEventHandlers;
|
|
50
|
+
/**
|
|
51
|
+
* Handle disconnection with exponential backoff retry
|
|
52
|
+
*/
|
|
53
|
+
private handleDisconnection;
|
|
54
|
+
/**
|
|
55
|
+
* Reconnect to MongoDB
|
|
56
|
+
*/
|
|
57
|
+
private reconnect;
|
|
58
|
+
/**
|
|
59
|
+
* Start health check monitoring
|
|
60
|
+
*/
|
|
61
|
+
private startHealthCheck;
|
|
62
|
+
/**
|
|
63
|
+
* Graceful shutdown
|
|
64
|
+
*/
|
|
65
|
+
private gracefulShutdown;
|
|
66
|
+
/**
|
|
67
|
+
* Connect to MongoDB with enhanced error handling and retry logic
|
|
68
|
+
*/
|
|
69
|
+
connectToDB(connectionString: string, serviceName: string, config?: ConnectionConfig): Promise<Connection>;
|
|
70
|
+
/**
|
|
71
|
+
* Get current connection state
|
|
72
|
+
*/
|
|
73
|
+
getConnectionState(): {
|
|
74
|
+
isConnected: boolean;
|
|
75
|
+
readyState: number;
|
|
76
|
+
readyStateText: string;
|
|
77
|
+
retryCount: number;
|
|
78
|
+
host?: string;
|
|
79
|
+
port?: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Check if database is healthy
|
|
83
|
+
*/
|
|
84
|
+
isHealthy(): Promise<boolean>;
|
|
85
|
+
}
|
|
86
|
+
declare const _default: MongooseConnection;
|
|
87
|
+
export default _default;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const error_handler_1 = require("@nimee/error-handler");
|
|
16
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
17
|
+
const logger_1 = __importDefault(require("@nimee/logger"));
|
|
18
|
+
let connectionInstance;
|
|
19
|
+
class MongooseConnection {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.db = null;
|
|
22
|
+
this.connectionString = "";
|
|
23
|
+
this.serviceName = "";
|
|
24
|
+
this.isConnecting = false;
|
|
25
|
+
this.retryCount = 0;
|
|
26
|
+
this.healthCheckInterval = null;
|
|
27
|
+
this.config = {
|
|
28
|
+
maxRetries: 10,
|
|
29
|
+
retryDelay: 5000,
|
|
30
|
+
healthCheckInterval: 30000,
|
|
31
|
+
enableHealthCheck: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
static getDefaultInstance() {
|
|
35
|
+
if (!connectionInstance) {
|
|
36
|
+
connectionInstance = new MongooseConnection();
|
|
37
|
+
}
|
|
38
|
+
return connectionInstance;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get production-ready Mongoose connection options
|
|
42
|
+
*/
|
|
43
|
+
getConnectionOptions() {
|
|
44
|
+
return {
|
|
45
|
+
// Connection pool settings
|
|
46
|
+
maxPoolSize: parseInt(process.env.MONGO_MAX_POOL_SIZE || "100"),
|
|
47
|
+
minPoolSize: parseInt(process.env.MONGO_MIN_POOL_SIZE || "10"),
|
|
48
|
+
// Timeout settings
|
|
49
|
+
serverSelectionTimeoutMS: parseInt(process.env.MONGO_SERVER_SELECTION_TIMEOUT || "30000"),
|
|
50
|
+
socketTimeoutMS: parseInt(process.env.MONGO_SOCKET_TIMEOUT || "45000"),
|
|
51
|
+
connectTimeoutMS: parseInt(process.env.MONGO_CONNECT_TIMEOUT || "30000"),
|
|
52
|
+
// Retry settings
|
|
53
|
+
retryWrites: true,
|
|
54
|
+
retryReads: true,
|
|
55
|
+
// Index management
|
|
56
|
+
autoIndex: process.env.NODE_ENV !== "production", // Disable in production
|
|
57
|
+
autoCreate: true,
|
|
58
|
+
// Connection monitoring
|
|
59
|
+
heartbeatFrequencyMS: parseInt(process.env.MONGO_HEARTBEAT_FREQUENCY || "10000"),
|
|
60
|
+
// Buffer settings
|
|
61
|
+
bufferCommands: false, // Don't buffer commands when disconnected
|
|
62
|
+
// Write concern
|
|
63
|
+
w: "majority",
|
|
64
|
+
journal: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Setup connection event handlers for monitoring and recovery
|
|
69
|
+
*/
|
|
70
|
+
setupEventHandlers() {
|
|
71
|
+
mongoose_1.default.connection.on("connected", () => {
|
|
72
|
+
logger_1.default.info(`[${this.serviceName}] MongoDB connected successfully`);
|
|
73
|
+
this.retryCount = 0;
|
|
74
|
+
});
|
|
75
|
+
mongoose_1.default.connection.on("disconnected", () => {
|
|
76
|
+
logger_1.default.warn(`[${this.serviceName}] MongoDB disconnected`);
|
|
77
|
+
if (!this.isConnecting) {
|
|
78
|
+
this.handleDisconnection();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
mongoose_1.default.connection.on("error", (error) => {
|
|
82
|
+
logger_1.default.error(`[${this.serviceName}] MongoDB connection error:`, error);
|
|
83
|
+
if (error.name === "MongoNetworkError" || error.name === "MongoServerError") {
|
|
84
|
+
this.handleDisconnection();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
mongoose_1.default.connection.on("reconnected", () => {
|
|
88
|
+
logger_1.default.info(`[${this.serviceName}] MongoDB reconnected successfully`);
|
|
89
|
+
this.retryCount = 0;
|
|
90
|
+
});
|
|
91
|
+
// Monitor replica set changes
|
|
92
|
+
mongoose_1.default.connection.on("reconnectFailed", () => {
|
|
93
|
+
logger_1.default.error(`[${this.serviceName}] MongoDB reconnection failed`);
|
|
94
|
+
this.handleDisconnection();
|
|
95
|
+
});
|
|
96
|
+
// Handle process termination
|
|
97
|
+
process.on("SIGINT", () => __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
yield this.gracefulShutdown("SIGINT");
|
|
99
|
+
}));
|
|
100
|
+
process.on("SIGTERM", () => __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
yield this.gracefulShutdown("SIGTERM");
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Handle disconnection with exponential backoff retry
|
|
106
|
+
*/
|
|
107
|
+
handleDisconnection() {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
if (this.isConnecting) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (this.retryCount >= (this.config.maxRetries || 10)) {
|
|
113
|
+
logger_1.default.error(`[${this.serviceName}] Max retry attempts (${this.config.maxRetries}) reached. Giving up.`);
|
|
114
|
+
process.exit(1); // Exit to allow container orchestration to restart
|
|
115
|
+
}
|
|
116
|
+
this.isConnecting = true;
|
|
117
|
+
this.retryCount++;
|
|
118
|
+
// Exponential backoff with jitter
|
|
119
|
+
const delay = Math.min((this.config.retryDelay || 5000) * Math.pow(2, this.retryCount - 1) + Math.random() * 1000, 60000 // Max 60 seconds
|
|
120
|
+
);
|
|
121
|
+
logger_1.default.info(`[${this.serviceName}] Attempting to reconnect (attempt ${this.retryCount}/${this.config.maxRetries}) in ${delay}ms...`);
|
|
122
|
+
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
try {
|
|
124
|
+
yield this.reconnect();
|
|
125
|
+
this.isConnecting = false;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
logger_1.default.error(`[${this.serviceName}] Reconnection attempt ${this.retryCount} failed: ${JSON.stringify(error)}`);
|
|
129
|
+
this.isConnecting = false;
|
|
130
|
+
this.handleDisconnection();
|
|
131
|
+
}
|
|
132
|
+
}), delay);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Reconnect to MongoDB
|
|
137
|
+
*/
|
|
138
|
+
reconnect() {
|
|
139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
+
try {
|
|
141
|
+
// Close existing connection if any
|
|
142
|
+
if (mongoose_1.default.connection.readyState !== 0) {
|
|
143
|
+
yield mongoose_1.default.connection.close();
|
|
144
|
+
}
|
|
145
|
+
// Attempt reconnection
|
|
146
|
+
yield mongoose_1.default.connect(this.connectionString, this.getConnectionOptions());
|
|
147
|
+
logger_1.default.info(`[${this.serviceName}] Successfully reconnected to MongoDB`);
|
|
148
|
+
this.db = mongoose_1.default.connection;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
throw new error_handler_1.CustomError(`${this.serviceName}_reconnection_failed`, 500, `Failed to reconnect to MongoDB: ${error.message}`, error);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Start health check monitoring
|
|
157
|
+
*/
|
|
158
|
+
startHealthCheck() {
|
|
159
|
+
if (!this.config.enableHealthCheck) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Clear existing interval if any
|
|
163
|
+
if (this.healthCheckInterval) {
|
|
164
|
+
clearInterval(this.healthCheckInterval);
|
|
165
|
+
}
|
|
166
|
+
this.healthCheckInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
167
|
+
var _a;
|
|
168
|
+
try {
|
|
169
|
+
if (mongoose_1.default.connection.readyState !== 1) {
|
|
170
|
+
logger_1.default.warn(`[${this.serviceName}] MongoDB health check failed - readyState: ${mongoose_1.default.connection.readyState}`);
|
|
171
|
+
this.handleDisconnection();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Ping the database
|
|
175
|
+
const adminDb = (_a = mongoose_1.default.connection.db) === null || _a === void 0 ? void 0 : _a.admin();
|
|
176
|
+
if (adminDb) {
|
|
177
|
+
const result = yield adminDb.ping();
|
|
178
|
+
if ((result === null || result === void 0 ? void 0 : result.ok) !== 1) {
|
|
179
|
+
logger_1.default.warn(`[${this.serviceName}] MongoDB ping failed`);
|
|
180
|
+
this.handleDisconnection();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
logger_1.default.error(`[${this.serviceName}] Health check error: ${JSON.stringify(error)}`);
|
|
186
|
+
this.handleDisconnection();
|
|
187
|
+
}
|
|
188
|
+
}), this.config.healthCheckInterval || 30000);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Graceful shutdown
|
|
192
|
+
*/
|
|
193
|
+
gracefulShutdown(signal) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
logger_1.default.info(`[${this.serviceName}] Received ${signal}, starting graceful shutdown...`);
|
|
196
|
+
// Stop health checks
|
|
197
|
+
if (this.healthCheckInterval) {
|
|
198
|
+
clearInterval(this.healthCheckInterval);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
yield mongoose_1.default.connection.close();
|
|
202
|
+
logger_1.default.info(`[${this.serviceName}] MongoDB connection closed gracefully`);
|
|
203
|
+
process.exit(0);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
logger_1.default.error(`[${this.serviceName}] Error during graceful shutdown: ${JSON.stringify(error)}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Connect to MongoDB with enhanced error handling and retry logic
|
|
213
|
+
*/
|
|
214
|
+
connectToDB(connectionString, serviceName, config) {
|
|
215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
try {
|
|
217
|
+
// Merge custom config with defaults
|
|
218
|
+
if (config) {
|
|
219
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
220
|
+
}
|
|
221
|
+
// Check if already connected
|
|
222
|
+
if (this.db && mongoose_1.default.connection.readyState === 1) {
|
|
223
|
+
logger_1.default.info(`[${serviceName}] MongoDB already connected`);
|
|
224
|
+
return this.db;
|
|
225
|
+
}
|
|
226
|
+
this.connectionString = connectionString;
|
|
227
|
+
this.serviceName = serviceName;
|
|
228
|
+
// Setup event handlers before connecting
|
|
229
|
+
this.setupEventHandlers();
|
|
230
|
+
// Attempt connection
|
|
231
|
+
logger_1.default.info(`[${serviceName}] Connecting to MongoDB...`);
|
|
232
|
+
yield mongoose_1.default.connect(connectionString, this.getConnectionOptions());
|
|
233
|
+
logger_1.default.info(`[${serviceName}] Connected successfully to MongoDB (last 10 chars: ${connectionString.slice(-10)})`);
|
|
234
|
+
this.db = mongoose_1.default.connection;
|
|
235
|
+
// Start health check monitoring
|
|
236
|
+
this.startHealthCheck();
|
|
237
|
+
return this.db;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
logger_1.default.error(`[${serviceName}] Initial connection failed: ${JSON.stringify(error)}`);
|
|
241
|
+
// Start retry mechanism
|
|
242
|
+
this.handleDisconnection();
|
|
243
|
+
throw new error_handler_1.CustomError(`${serviceName}_mongoose_connection_failed`, 500, `Failed to connect to MongoDB: ${error.message}`, error);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get current connection state
|
|
249
|
+
*/
|
|
250
|
+
getConnectionState() {
|
|
251
|
+
const readyStates = {
|
|
252
|
+
0: "disconnected",
|
|
253
|
+
1: "connected",
|
|
254
|
+
2: "connecting",
|
|
255
|
+
3: "disconnecting"
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
isConnected: mongoose_1.default.connection.readyState === 1,
|
|
259
|
+
readyState: mongoose_1.default.connection.readyState,
|
|
260
|
+
readyStateText: readyStates[mongoose_1.default.connection.readyState] || "unknown",
|
|
261
|
+
retryCount: this.retryCount,
|
|
262
|
+
host: mongoose_1.default.connection.host,
|
|
263
|
+
port: mongoose_1.default.connection.port
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check if database is healthy
|
|
268
|
+
*/
|
|
269
|
+
isHealthy() {
|
|
270
|
+
var _a;
|
|
271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
272
|
+
try {
|
|
273
|
+
if (mongoose_1.default.connection.readyState !== 1) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
// Try to ping the database
|
|
277
|
+
const adminDb = (_a = mongoose_1.default.connection.db) === null || _a === void 0 ? void 0 : _a.admin();
|
|
278
|
+
if (adminDb) {
|
|
279
|
+
const result = yield adminDb.ping();
|
|
280
|
+
return (result === null || result === void 0 ? void 0 : result.ok) === 1;
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
logger_1.default.error("Health check failed: ${JSON.stringify(error)}");
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.default = MongooseConnection.getDefaultInstance();
|
|
292
|
+
//# sourceMappingURL=mongooseConnectionV2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongooseConnectionV2.js","sourceRoot":"","sources":["../../src/mongooseConnectionV2.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,wDAAmD;AACnD,wDAAgE;AAChE,2DAAmC;AAEnC,IAAI,kBAAsC,CAAC;AAS3C,MAAM,kBAAkB;IAAxB;QACU,OAAE,GAAsB,IAAI,CAAC;QAC7B,qBAAgB,GAAW,EAAE,CAAC;QAC9B,gBAAW,GAAW,EAAE,CAAC;QACzB,iBAAY,GAAY,KAAK,CAAC;QAC9B,eAAU,GAAW,CAAC,CAAC;QACvB,wBAAmB,GAA0B,IAAI,CAAC;QAClD,WAAM,GAAqB;YACjC,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,IAAI;YAChB,mBAAmB,EAAE,KAAK;YAC1B,iBAAiB,EAAE,IAAI;SACxB,CAAC;IAsTJ,CAAC;IApTC,MAAM,CAAC,kBAAkB;QACvB,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAC;QAChD,CAAC;QACD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,OAAO;YACL,2BAA2B;YAC3B,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,KAAK,CAAC;YAC/D,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC;YAE9D,mBAAmB;YACnB,wBAAwB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,OAAO,CAAC;YACzF,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC;YACtE,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,CAAC;YAExE,iBAAiB;YACjB,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,IAAI;YAEhB,mBAAmB;YACnB,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,wBAAwB;YAC1E,UAAU,EAAE,IAAI;YAEhB,wBAAwB;YACxB,oBAAoB,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,OAAO,CAAC;YAEhF,kBAAkB;YAClB,cAAc,EAAE,KAAK,EAAE,0CAA0C;YAEjE,gBAAgB;YAChB,CAAC,EAAE,UAAU;YACb,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,kBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;YACvC,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,kCAAkC,CAAC,CAAC;YACpE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,kBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;YAC1C,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,wBAAwB,CAAC,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACvB,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,kBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACxC,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,6BAA6B,EAAE,KAAK,CAAC,CAAC;YACvE,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC5E,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,kBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,GAAG,EAAE;YACzC,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,oCAAoC,CAAC,CAAC;YACtE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,8BAA8B;QAC9B,kBAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE;YAC7C,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,+BAA+B,CAAC,CAAC;YAClE,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,6BAA6B;QAC7B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAS,EAAE;YAC9B,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC,CAAA,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAS,EAAE;YAC/B,MAAM,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC,CAAA,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACW,mBAAmB;;YAC/B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,OAAO;YACT,CAAC;YAED,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;gBACtD,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,yBAAyB,IAAI,CAAC,MAAM,CAAC,UAAU,uBAAuB,CAAC,CAAC;gBACzG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,mDAAmD;YACtE,CAAC;YAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,UAAU,EAAE,CAAC;YAElB,kCAAkC;YAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAC1F,KAAK,CAAC,iBAAiB;aACxB,CAAC;YAEF,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,sCAAsC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,QAAQ,KAAK,OAAO,CAAC,CAAC;YAErI,UAAU,CAAC,GAAS,EAAE;gBACpB,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC5B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,0BAA0B,IAAI,CAAC,UAAU,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC/G,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;oBAC1B,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,CAAC;YACH,CAAC,CAAA,EAAE,KAAK,CAAC,CAAC;QACZ,CAAC;KAAA;IAED;;OAEG;IACW,SAAS;;YACrB,IAAI,CAAC;gBACH,mCAAmC;gBACnC,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACzC,MAAM,kBAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpC,CAAC;gBAED,uBAAuB;gBACvB,MAAM,kBAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;gBAC3E,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,uCAAuC,CAAC,CAAC;gBACzE,IAAI,CAAC,EAAE,GAAG,kBAAQ,CAAC,UAAU,CAAC;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,2BAAW,CACnB,GAAG,IAAI,CAAC,WAAW,sBAAsB,EACzC,GAAG,EACH,mCAAmC,KAAK,CAAC,OAAO,EAAE,EAClD,KAAK,CACN,CAAC;YACJ,CAAC;QACH,CAAC;KAAA;IAED;;OAEG;IACK,gBAAgB;QACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACnC,OAAO;QACT,CAAC;QAED,iCAAiC;QACjC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,aAAa,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC1C,CAAC;QAED,IAAI,CAAC,mBAAmB,GAAG,WAAW,CAAC,GAAS,EAAE;;YAChD,IAAI,CAAC;gBACH,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACzC,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,+CAA+C,kBAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;oBACjH,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBAC3B,OAAO;gBACT,CAAC;gBAED,oBAAoB;gBACpB,MAAM,OAAO,GAAG,MAAA,kBAAQ,CAAC,UAAU,CAAC,EAAE,0CAAE,KAAK,EAAE,CAAC;gBAChD,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;oBACpC,IAAI,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,EAAE,MAAK,CAAC,EAAE,CAAC;wBACrB,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,uBAAuB,CAAC,CAAC;wBACzD,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBAC7B,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,yBAAyB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACnF,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC,CAAA,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,IAAI,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACW,gBAAgB,CAAC,MAAc;;YAC3C,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,cAAc,MAAM,iCAAiC,CAAC,CAAC;YAEvF,qBAAqB;YACrB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,aAAa,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC1C,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,kBAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAClC,gBAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,wCAAwC,CAAC,CAAC;gBAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gBAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,qCAAqC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;KAAA;IAED;;OAEG;IACG,WAAW,CACf,gBAAwB,EACxB,WAAmB,EACnB,MAAyB;;YAEzB,IAAI,CAAC;gBACH,oCAAoC;gBACpC,IAAI,MAAM,EAAE,CAAC;oBACX,IAAI,CAAC,MAAM,mCAAQ,IAAI,CAAC,MAAM,GAAK,MAAM,CAAE,CAAC;gBAC9C,CAAC;gBAED,6BAA6B;gBAC7B,IAAI,IAAI,CAAC,EAAE,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACpD,gBAAM,CAAC,IAAI,CAAC,IAAI,WAAW,6BAA6B,CAAC,CAAC;oBAC1D,OAAO,IAAI,CAAC,EAAE,CAAC;gBACjB,CAAC;gBAED,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;gBACzC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;gBAE/B,yCAAyC;gBACzC,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAE1B,qBAAqB;gBACrB,gBAAM,CAAC,IAAI,CAAC,IAAI,WAAW,4BAA4B,CAAC,CAAC;gBACzD,MAAM,kBAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;gBAEtE,gBAAM,CAAC,IAAI,CACT,IAAI,WAAW,uDAAuD,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CACrG,CAAC;gBAEF,IAAI,CAAC,EAAE,GAAG,kBAAQ,CAAC,UAAU,CAAC;gBAE9B,gCAAgC;gBAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAExB,OAAO,IAAI,CAAC,EAAE,CAAC;YACjB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gBAAM,CAAC,KAAK,CAAC,IAAI,WAAW,gCAAgC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAErF,wBAAwB;gBACxB,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAE3B,MAAM,IAAI,2BAAW,CACnB,GAAG,WAAW,6BAA6B,EAC3C,GAAG,EACH,iCAAiC,KAAK,CAAC,OAAO,EAAE,EAChD,KAAK,CACN,CAAC;YACJ,CAAC;QACH,CAAC;KAAA;IAED;;OAEG;IACH,kBAAkB;QAQhB,MAAM,WAAW,GAAG;YAClB,CAAC,EAAE,cAAc;YACjB,CAAC,EAAE,WAAW;YACd,CAAC,EAAE,YAAY;YACf,CAAC,EAAE,eAAe;SACnB,CAAC;QAEF,OAAO;YACL,WAAW,EAAE,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC;YACjD,UAAU,EAAE,kBAAQ,CAAC,UAAU,CAAC,UAAU;YAC1C,cAAc,EAAE,WAAW,CAAC,kBAAQ,CAAC,UAAU,CAAC,UAAsC,CAAC,IAAI,SAAS;YACpG,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI,EAAE,kBAAQ,CAAC,UAAU,CAAC,IAAI;YAC9B,IAAI,EAAE,kBAAQ,CAAC,UAAU,CAAC,IAAI;SAC/B,CAAC;IACJ,CAAC;IAED;;OAEG;IACG,SAAS;;;YACb,IAAI,CAAC;gBACH,IAAI,kBAAQ,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACzC,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,2BAA2B;gBAC3B,MAAM,OAAO,GAAG,MAAA,kBAAQ,CAAC,UAAU,CAAC,EAAE,0CAAE,KAAK,EAAE,CAAC;gBAChD,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;oBACpC,OAAO,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,EAAE,MAAK,CAAC,CAAC;gBAC1B,CAAC;gBAED,OAAO,KAAK,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gBAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBAC9D,OAAO,KAAK,CAAC;YACf,CAAC;;KACF;CACF;AAED,kBAAe,kBAAkB,CAAC,kBAAkB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimee/initialize-ms",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.77",
|
|
4
4
|
"description": "init behavior for each ms",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"author": "dan goldberg",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@fastify/swagger": "^8.1.0",
|
|
36
36
|
"@fastify/swagger-ui": "^1.3.0",
|
|
37
37
|
"@nimee/error-handler": "^0.0.12",
|
|
38
|
-
"@nimee/health": "2.1.
|
|
38
|
+
"@nimee/health": "2.1.5",
|
|
39
39
|
"@nimee/logger": "^1.0.27",
|
|
40
40
|
"@nimee/metrics": "2.0.14",
|
|
41
41
|
"@opentelemetry/api": "^1.7.0",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"@opentelemetry/sdk-trace-base": "^1.20.0",
|
|
50
50
|
"@opentelemetry/sdk-trace-node": "^1.21.0",
|
|
51
51
|
"fastify": "4.24.3",
|
|
52
|
-
"mongoose": "
|
|
52
|
+
"mongoose": "^8.19.2"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/index.ts
CHANGED
|
@@ -28,8 +28,9 @@ if (process.env.NODE_ENV !== "local" && process.env.NODE_ENV !== "dev") {
|
|
|
28
28
|
import ServerInitializerFastify from "./fastify";
|
|
29
29
|
import logger from "@nimee/logger";
|
|
30
30
|
import { errorHandler } from "@nimee/error-handler";
|
|
31
|
-
import HealthService from "@nimee/health";
|
|
31
|
+
import HealthService, { HealthServiceV2 } from "@nimee/health";
|
|
32
32
|
import { defaultMetricsExporter } from "@nimee/metrics";
|
|
33
|
+
import mongooseConnectionV2 from "./mongooseConnectionV2";
|
|
33
34
|
|
|
34
35
|
class AppInitializer {
|
|
35
36
|
public httpServer: any;
|
|
@@ -65,8 +66,36 @@ class AppInitializer {
|
|
|
65
66
|
// await redisService.initRedisConnection(this.serviceName, `${this.serviceName}_`);
|
|
66
67
|
}
|
|
67
68
|
errorHandler.registerAndHandleAllErrors(this.server);
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
// Store httpServer reference for HealthServiceV2
|
|
71
|
+
this.httpServer = this.server.server;
|
|
72
|
+
|
|
73
|
+
// Choose health service version based on blacklist
|
|
74
|
+
if (blackFeatureList.includes("healthV1")) {
|
|
75
|
+
// Use enhanced HealthServiceV2 with MongoDB health checks
|
|
76
|
+
logger.info(`[${this.serviceName}] Setting up HealthServiceV2 with MongoDB monitoring`);
|
|
77
|
+
const healthServiceV2 = new HealthServiceV2(
|
|
78
|
+
this.server,
|
|
79
|
+
this.httpServer,
|
|
80
|
+
{
|
|
81
|
+
livenessPath: "/liveness",
|
|
82
|
+
readinessPath: "/readiness",
|
|
83
|
+
healthPath: "/health",
|
|
84
|
+
includeDbCheck: true,
|
|
85
|
+
dbTimeout: 5000
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
healthServiceV2.start(this.serviceName);
|
|
89
|
+
// Use defaultMetricsExporter for consistent Prometheus metrics
|
|
90
|
+
defaultMetricsExporter.collectDefaultMetricsAndExport(this.server, "/metrics", 2000, process.env.logLevel);
|
|
91
|
+
// Setup periodic health logging if enabled
|
|
92
|
+
this.setupPeriodicHealthLogging();
|
|
93
|
+
} else {
|
|
94
|
+
// Use default HealthService V1 (backward compatible)
|
|
95
|
+
logger.info(`[${this.serviceName}] Setting up default HealthService V1`);
|
|
96
|
+
await new HealthService(this.server, undefined).start(this.serviceName);
|
|
97
|
+
defaultMetricsExporter.collectDefaultMetricsAndExport(this.server, "/metrics", 2000, process.env.logLevel);
|
|
98
|
+
}
|
|
70
99
|
|
|
71
100
|
if (entryPointsInitializer) {
|
|
72
101
|
const allEntryPointInitializers = [entryPointsInitializer];
|
|
@@ -78,7 +107,40 @@ class AppInitializer {
|
|
|
78
107
|
}
|
|
79
108
|
if (serverType) await fastify.listen(port);
|
|
80
109
|
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Setup periodic health status logging (optional)
|
|
113
|
+
* Only active when LOG_HEALTH_STATUS=true and using V2 connection
|
|
114
|
+
*/
|
|
115
|
+
private setupPeriodicHealthLogging() {
|
|
116
|
+
if (process.env.LOG_HEALTH_STATUS === "true") {
|
|
117
|
+
logger.info(`[${this.serviceName}] Enabling periodic health status logging (every 60s)`);
|
|
118
|
+
setInterval(async () => {
|
|
119
|
+
try {
|
|
120
|
+
const isHealthy = await mongooseConnectionV2.isHealthy();
|
|
121
|
+
const state = mongooseConnectionV2.getConnectionState();
|
|
122
|
+
|
|
123
|
+
// Always log health status for monitoring
|
|
124
|
+
logger.info(`[${this.serviceName}] Health check result: ${JSON.stringify({
|
|
125
|
+
healthy: isHealthy,
|
|
126
|
+
readyState: state.readyState,
|
|
127
|
+
readyStateText: state.readyStateText,
|
|
128
|
+
retryCount: state.retryCount,
|
|
129
|
+
timestamp: new Date().toISOString()
|
|
130
|
+
})}`);
|
|
131
|
+
|
|
132
|
+
// Still warn if unhealthy
|
|
133
|
+
if (!isHealthy || state.readyState !== 1) {
|
|
134
|
+
logger.warn(`[${this.serviceName}] MongoDB is unhealthy!`);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
logger.error(`[${this.serviceName}] Health check error: ${JSON.stringify(error)}`);
|
|
138
|
+
}
|
|
139
|
+
}, 60000); // Check every minute
|
|
140
|
+
}
|
|
141
|
+
}
|
|
81
142
|
}
|
|
82
143
|
|
|
83
144
|
export default AppInitializer;
|
|
84
145
|
export { TracerCoralogix };
|
|
146
|
+
export { default as mongooseConnectionV2 } from "./mongooseConnectionV2";
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { CustomError } from "@nimee/error-handler";
|
|
2
|
+
import mongoose, { Connection, ConnectOptions } from "mongoose";
|
|
3
|
+
import logger from "@nimee/logger";
|
|
4
|
+
|
|
5
|
+
let connectionInstance: MongooseConnection;
|
|
6
|
+
|
|
7
|
+
interface ConnectionConfig {
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
retryDelay?: number;
|
|
10
|
+
healthCheckInterval?: number;
|
|
11
|
+
enableHealthCheck?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class MongooseConnection {
|
|
15
|
+
private db: Connection | null = null;
|
|
16
|
+
private connectionString: string = "";
|
|
17
|
+
private serviceName: string = "";
|
|
18
|
+
private isConnecting: boolean = false;
|
|
19
|
+
private retryCount: number = 0;
|
|
20
|
+
private healthCheckInterval: NodeJS.Timeout | null = null;
|
|
21
|
+
private config: ConnectionConfig = {
|
|
22
|
+
maxRetries: 10,
|
|
23
|
+
retryDelay: 5000,
|
|
24
|
+
healthCheckInterval: 30000,
|
|
25
|
+
enableHealthCheck: true
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
static getDefaultInstance(): MongooseConnection {
|
|
29
|
+
if (!connectionInstance) {
|
|
30
|
+
connectionInstance = new MongooseConnection();
|
|
31
|
+
}
|
|
32
|
+
return connectionInstance;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get production-ready Mongoose connection options
|
|
37
|
+
*/
|
|
38
|
+
private getConnectionOptions(): ConnectOptions {
|
|
39
|
+
return {
|
|
40
|
+
// Connection pool settings
|
|
41
|
+
maxPoolSize: parseInt(process.env.MONGO_MAX_POOL_SIZE || "100"),
|
|
42
|
+
minPoolSize: parseInt(process.env.MONGO_MIN_POOL_SIZE || "10"),
|
|
43
|
+
|
|
44
|
+
// Timeout settings
|
|
45
|
+
serverSelectionTimeoutMS: parseInt(process.env.MONGO_SERVER_SELECTION_TIMEOUT || "30000"),
|
|
46
|
+
socketTimeoutMS: parseInt(process.env.MONGO_SOCKET_TIMEOUT || "45000"),
|
|
47
|
+
connectTimeoutMS: parseInt(process.env.MONGO_CONNECT_TIMEOUT || "30000"),
|
|
48
|
+
|
|
49
|
+
// Retry settings
|
|
50
|
+
retryWrites: true,
|
|
51
|
+
retryReads: true,
|
|
52
|
+
|
|
53
|
+
// Index management
|
|
54
|
+
autoIndex: process.env.NODE_ENV !== "production", // Disable in production
|
|
55
|
+
autoCreate: true,
|
|
56
|
+
|
|
57
|
+
// Connection monitoring
|
|
58
|
+
heartbeatFrequencyMS: parseInt(process.env.MONGO_HEARTBEAT_FREQUENCY || "10000"),
|
|
59
|
+
|
|
60
|
+
// Buffer settings
|
|
61
|
+
bufferCommands: false, // Don't buffer commands when disconnected
|
|
62
|
+
|
|
63
|
+
// Write concern
|
|
64
|
+
w: "majority",
|
|
65
|
+
journal: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Setup connection event handlers for monitoring and recovery
|
|
71
|
+
*/
|
|
72
|
+
private setupEventHandlers(): void {
|
|
73
|
+
mongoose.connection.on("connected", () => {
|
|
74
|
+
logger.info(`[${this.serviceName}] MongoDB connected successfully`);
|
|
75
|
+
this.retryCount = 0;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
mongoose.connection.on("disconnected", () => {
|
|
79
|
+
logger.warn(`[${this.serviceName}] MongoDB disconnected`);
|
|
80
|
+
if (!this.isConnecting) {
|
|
81
|
+
this.handleDisconnection();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
mongoose.connection.on("error", (error) => {
|
|
86
|
+
logger.error(`[${this.serviceName}] MongoDB connection error:`, error);
|
|
87
|
+
if (error.name === "MongoNetworkError" || error.name === "MongoServerError") {
|
|
88
|
+
this.handleDisconnection();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
mongoose.connection.on("reconnected", () => {
|
|
93
|
+
logger.info(`[${this.serviceName}] MongoDB reconnected successfully`);
|
|
94
|
+
this.retryCount = 0;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Monitor replica set changes
|
|
98
|
+
mongoose.connection.on("reconnectFailed", () => {
|
|
99
|
+
logger.error(`[${this.serviceName}] MongoDB reconnection failed`);
|
|
100
|
+
this.handleDisconnection();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Handle process termination
|
|
104
|
+
process.on("SIGINT", async () => {
|
|
105
|
+
await this.gracefulShutdown("SIGINT");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
process.on("SIGTERM", async () => {
|
|
109
|
+
await this.gracefulShutdown("SIGTERM");
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handle disconnection with exponential backoff retry
|
|
115
|
+
*/
|
|
116
|
+
private async handleDisconnection(): Promise<void> {
|
|
117
|
+
if (this.isConnecting) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.retryCount >= (this.config.maxRetries || 10)) {
|
|
122
|
+
logger.error(`[${this.serviceName}] Max retry attempts (${this.config.maxRetries}) reached. Giving up.`);
|
|
123
|
+
process.exit(1); // Exit to allow container orchestration to restart
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.isConnecting = true;
|
|
127
|
+
this.retryCount++;
|
|
128
|
+
|
|
129
|
+
// Exponential backoff with jitter
|
|
130
|
+
const delay = Math.min(
|
|
131
|
+
(this.config.retryDelay || 5000) * Math.pow(2, this.retryCount - 1) + Math.random() * 1000,
|
|
132
|
+
60000 // Max 60 seconds
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
logger.info(`[${this.serviceName}] Attempting to reconnect (attempt ${this.retryCount}/${this.config.maxRetries}) in ${delay}ms...`);
|
|
136
|
+
|
|
137
|
+
setTimeout(async () => {
|
|
138
|
+
try {
|
|
139
|
+
await this.reconnect();
|
|
140
|
+
this.isConnecting = false;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error(`[${this.serviceName}] Reconnection attempt ${this.retryCount} failed: ${JSON.stringify(error)}`);
|
|
143
|
+
this.isConnecting = false;
|
|
144
|
+
this.handleDisconnection();
|
|
145
|
+
}
|
|
146
|
+
}, delay);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Reconnect to MongoDB
|
|
151
|
+
*/
|
|
152
|
+
private async reconnect(): Promise<void> {
|
|
153
|
+
try {
|
|
154
|
+
// Close existing connection if any
|
|
155
|
+
if (mongoose.connection.readyState !== 0) {
|
|
156
|
+
await mongoose.connection.close();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Attempt reconnection
|
|
160
|
+
await mongoose.connect(this.connectionString, this.getConnectionOptions());
|
|
161
|
+
logger.info(`[${this.serviceName}] Successfully reconnected to MongoDB`);
|
|
162
|
+
this.db = mongoose.connection;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new CustomError(
|
|
165
|
+
`${this.serviceName}_reconnection_failed`,
|
|
166
|
+
500,
|
|
167
|
+
`Failed to reconnect to MongoDB: ${error.message}`,
|
|
168
|
+
error
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Start health check monitoring
|
|
175
|
+
*/
|
|
176
|
+
private startHealthCheck(): void {
|
|
177
|
+
if (!this.config.enableHealthCheck) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Clear existing interval if any
|
|
182
|
+
if (this.healthCheckInterval) {
|
|
183
|
+
clearInterval(this.healthCheckInterval);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.healthCheckInterval = setInterval(async () => {
|
|
187
|
+
try {
|
|
188
|
+
if (mongoose.connection.readyState !== 1) {
|
|
189
|
+
logger.warn(`[${this.serviceName}] MongoDB health check failed - readyState: ${mongoose.connection.readyState}`);
|
|
190
|
+
this.handleDisconnection();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Ping the database
|
|
195
|
+
const adminDb = mongoose.connection.db?.admin();
|
|
196
|
+
if (adminDb) {
|
|
197
|
+
const result = await adminDb.ping();
|
|
198
|
+
if (result?.ok !== 1) {
|
|
199
|
+
logger.warn(`[${this.serviceName}] MongoDB ping failed`);
|
|
200
|
+
this.handleDisconnection();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
logger.error(`[${this.serviceName}] Health check error: ${JSON.stringify(error)}`);
|
|
205
|
+
this.handleDisconnection();
|
|
206
|
+
}
|
|
207
|
+
}, this.config.healthCheckInterval || 30000);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Graceful shutdown
|
|
212
|
+
*/
|
|
213
|
+
private async gracefulShutdown(signal: string): Promise<void> {
|
|
214
|
+
logger.info(`[${this.serviceName}] Received ${signal}, starting graceful shutdown...`);
|
|
215
|
+
|
|
216
|
+
// Stop health checks
|
|
217
|
+
if (this.healthCheckInterval) {
|
|
218
|
+
clearInterval(this.healthCheckInterval);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await mongoose.connection.close();
|
|
223
|
+
logger.info(`[${this.serviceName}] MongoDB connection closed gracefully`);
|
|
224
|
+
process.exit(0);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
logger.error(`[${this.serviceName}] Error during graceful shutdown: ${JSON.stringify(error)}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Connect to MongoDB with enhanced error handling and retry logic
|
|
233
|
+
*/
|
|
234
|
+
async connectToDB(
|
|
235
|
+
connectionString: string,
|
|
236
|
+
serviceName: string,
|
|
237
|
+
config?: ConnectionConfig
|
|
238
|
+
): Promise<Connection> {
|
|
239
|
+
try {
|
|
240
|
+
// Merge custom config with defaults
|
|
241
|
+
if (config) {
|
|
242
|
+
this.config = { ...this.config, ...config };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Check if already connected
|
|
246
|
+
if (this.db && mongoose.connection.readyState === 1) {
|
|
247
|
+
logger.info(`[${serviceName}] MongoDB already connected`);
|
|
248
|
+
return this.db;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
this.connectionString = connectionString;
|
|
252
|
+
this.serviceName = serviceName;
|
|
253
|
+
|
|
254
|
+
// Setup event handlers before connecting
|
|
255
|
+
this.setupEventHandlers();
|
|
256
|
+
|
|
257
|
+
// Attempt connection
|
|
258
|
+
logger.info(`[${serviceName}] Connecting to MongoDB...`);
|
|
259
|
+
await mongoose.connect(connectionString, this.getConnectionOptions());
|
|
260
|
+
|
|
261
|
+
logger.info(
|
|
262
|
+
`[${serviceName}] Connected successfully to MongoDB (last 10 chars: ${connectionString.slice(-10)})`
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
this.db = mongoose.connection;
|
|
266
|
+
|
|
267
|
+
// Start health check monitoring
|
|
268
|
+
this.startHealthCheck();
|
|
269
|
+
|
|
270
|
+
return this.db;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
logger.error(`[${serviceName}] Initial connection failed: ${JSON.stringify(error)}`);
|
|
273
|
+
|
|
274
|
+
// Start retry mechanism
|
|
275
|
+
this.handleDisconnection();
|
|
276
|
+
|
|
277
|
+
throw new CustomError(
|
|
278
|
+
`${serviceName}_mongoose_connection_failed`,
|
|
279
|
+
500,
|
|
280
|
+
`Failed to connect to MongoDB: ${error.message}`,
|
|
281
|
+
error
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get current connection state
|
|
288
|
+
*/
|
|
289
|
+
getConnectionState(): {
|
|
290
|
+
isConnected: boolean;
|
|
291
|
+
readyState: number;
|
|
292
|
+
readyStateText: string;
|
|
293
|
+
retryCount: number;
|
|
294
|
+
host?: string;
|
|
295
|
+
port?: number;
|
|
296
|
+
} {
|
|
297
|
+
const readyStates = {
|
|
298
|
+
0: "disconnected",
|
|
299
|
+
1: "connected",
|
|
300
|
+
2: "connecting",
|
|
301
|
+
3: "disconnecting"
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
isConnected: mongoose.connection.readyState === 1,
|
|
306
|
+
readyState: mongoose.connection.readyState,
|
|
307
|
+
readyStateText: readyStates[mongoose.connection.readyState as keyof typeof readyStates] || "unknown",
|
|
308
|
+
retryCount: this.retryCount,
|
|
309
|
+
host: mongoose.connection.host,
|
|
310
|
+
port: mongoose.connection.port
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Check if database is healthy
|
|
316
|
+
*/
|
|
317
|
+
async isHealthy(): Promise<boolean> {
|
|
318
|
+
try {
|
|
319
|
+
if (mongoose.connection.readyState !== 1) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Try to ping the database
|
|
324
|
+
const adminDb = mongoose.connection.db?.admin();
|
|
325
|
+
if (adminDb) {
|
|
326
|
+
const result = await adminDb.ping();
|
|
327
|
+
return result?.ok === 1;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return false;
|
|
331
|
+
} catch (error) {
|
|
332
|
+
logger.error("Health check failed: ${JSON.stringify(error)}");
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export default MongooseConnection.getDefaultInstance();
|
package/dist/src/tracer.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare class Tracer {
|
|
2
|
-
private provider;
|
|
3
|
-
init({ serviceName, url, environment, basicAuth, helios, }: {
|
|
4
|
-
serviceName: string;
|
|
5
|
-
url: string;
|
|
6
|
-
environment: string;
|
|
7
|
-
basicAuth: string;
|
|
8
|
-
helios: any;
|
|
9
|
-
}): void;
|
|
10
|
-
}
|
|
11
|
-
declare const _default: Tracer;
|
|
12
|
-
export default _default;
|
package/dist/src/tracer.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const exporter_trace_otlp_grpc_1 = require("@opentelemetry/exporter-trace-otlp-grpc");
|
|
7
|
-
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
|
|
8
|
-
const grpc = require("@grpc/grpc-js");
|
|
9
|
-
const logger_1 = __importDefault(require("@nimee/logger"));
|
|
10
|
-
class Tracer {
|
|
11
|
-
init({ serviceName, url, environment, basicAuth, helios, }) {
|
|
12
|
-
try {
|
|
13
|
-
const metadata = new grpc.Metadata();
|
|
14
|
-
// after "Basic", comes the output of:
|
|
15
|
-
// echo -n username:password | base64 -w 0
|
|
16
|
-
metadata.set("authorization", basicAuth);
|
|
17
|
-
const exporter = new exporter_trace_otlp_grpc_1.OTLPTraceExporter({
|
|
18
|
-
url: url,
|
|
19
|
-
credentials: grpc.credentials.createSsl(),
|
|
20
|
-
metadata,
|
|
21
|
-
});
|
|
22
|
-
this.provider = helios;
|
|
23
|
-
// this.provider = new BasicTracerProvider({
|
|
24
|
-
// resource: new Resource({
|
|
25
|
-
// [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
|
|
26
|
-
// [SemanticResourceAttributes.SERVICE_VERSION]: "1.0.0",
|
|
27
|
-
// [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: environment,
|
|
28
|
-
// [SemanticResourceAttributes.TELEMETRY_SDK_LANGUAGE]: "node",
|
|
29
|
-
// }),
|
|
30
|
-
// });
|
|
31
|
-
// export spans to console (useful for debugging)s
|
|
32
|
-
// this.provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
|
|
33
|
-
// export spans to opentelemetry collector
|
|
34
|
-
this.provider.addSpanProcessor(new sdk_trace_node_1.SimpleSpanProcessor(exporter));
|
|
35
|
-
// this.provider.register();
|
|
36
|
-
// this.sdk = new NodeSDK({
|
|
37
|
-
// traceExporter: exporter,
|
|
38
|
-
// instrumentations: [
|
|
39
|
-
// getNodeAutoInstrumentations({
|
|
40
|
-
// // Lets disable fs for now, otherwise we cannot see the traces we want,
|
|
41
|
-
// // You can disable or enable instrumentation as needed
|
|
42
|
-
// "@opentelemetry/instrumentation-fs": { enabled: false },
|
|
43
|
-
// }),
|
|
44
|
-
// ],
|
|
45
|
-
// });
|
|
46
|
-
// this.sdk.start();
|
|
47
|
-
logger_1.default.info(`The tracer has been initialized with the service name: ${serviceName} and node environment: ${environment}`);
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
logger_1.default.error(`Failed to initialize the trace with error: ${e}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
exports.default = new Tracer();
|
|
55
|
-
//# sourceMappingURL=tracer.js.map
|
package/dist/src/tracer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tracer.js","sourceRoot":"","sources":["../../src/tracer.ts"],"names":[],"mappings":";;;;;AAAA,sFAA4E;AAC5E,kEAAoE;AACpE,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEtC,2DAAmC;AACnC,MAAM,MAAM;IAGH,IAAI,CAAC,EACV,WAAW,EACX,GAAG,EACH,WAAW,EACX,SAAS,EACT,MAAM,GAOP;QACC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,sCAAsC;YACtC,0CAA0C;YAC1C,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;YAEzC,MAAM,QAAQ,GAAG,IAAI,4CAAiB,CAAC;gBACrC,GAAG,EAAE,GAAG;gBACR,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE;gBACzC,QAAQ;aACT,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;YAEvB,4CAA4C;YAC5C,6BAA6B;YAC7B,8DAA8D;YAC9D,6DAA6D;YAC7D,wEAAwE;YACxE,mEAAmE;YACnE,QAAQ;YACR,MAAM;YACN,kDAAkD;YAClD,sFAAsF;YACtF,0CAA0C;YAC1C,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,oCAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClE,4BAA4B;YAC5B,2BAA2B;YAC3B,6BAA6B;YAC7B,wBAAwB;YACxB,oCAAoC;YACpC,gFAAgF;YAChF,+DAA+D;YAC/D,iEAAiE;YACjE,UAAU;YACV,OAAO;YACP,MAAM;YAEN,oBAAoB;YAEpB,gBAAM,CAAC,IAAI,CAAC,0DAA0D,WAAW,0BAA0B,WAAW,EAAE,CAAC,CAAC;QAC5H,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,gBAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;CACF;AAED,kBAAe,IAAI,MAAM,EAAE,CAAC"}
|