@abyss-project/monitor 1.0.55 → 1.0.57

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.
@@ -1,3 +1,4 @@
1
1
  export * from './context.middleware';
2
2
  export * from './logger-endpoint.middleware';
3
3
  export * from './logger-setup.middleware';
4
+ export * from './request-tracker.middleware';
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./context.middleware"), exports);
18
18
  __exportStar(require("./logger-endpoint.middleware"), exports);
19
19
  __exportStar(require("./logger-setup.middleware"), exports);
20
+ __exportStar(require("./request-tracker.middleware"), exports);
@@ -0,0 +1,3 @@
1
+ import { Request, Response, NextFunction } from 'express-serve-static-core';
2
+ import { Logger } from '../models/logger.model';
3
+ export declare const createRequestTrackerMiddleware: (logger?: Logger) => (req: Request, res: Response, next: NextFunction) => void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRequestTrackerMiddleware = void 0;
4
+ const request_tracker_utils_1 = require("../utils/request-tracker.utils");
5
+ const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
6
+ const createRequestTrackerMiddleware = (logger) => {
7
+ return (req, res, next) => {
8
+ if ((0, request_tracker_utils_1.getIsShuttingDown)()) {
9
+ res.status(503).json({
10
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.SERVICE_UNAVAILABLE,
11
+ message: graceful_shutdown_types_1.GracefulShutdownErrorMessage[graceful_shutdown_types_1.GracefulShutdownErrorCode.SERVICE_UNAVAILABLE],
12
+ });
13
+ return;
14
+ }
15
+ (0, request_tracker_utils_1.incrementActiveRequests)();
16
+ let cleaned = false;
17
+ const cleanup = () => {
18
+ if (cleaned)
19
+ return;
20
+ cleaned = true;
21
+ (0, request_tracker_utils_1.decrementActiveRequests)();
22
+ };
23
+ res.on('finish', cleanup);
24
+ res.on('close', cleanup);
25
+ next();
26
+ };
27
+ };
28
+ exports.createRequestTrackerMiddleware = createRequestTrackerMiddleware;
@@ -0,0 +1,29 @@
1
+ import { Logger } from '../models/logger.model';
2
+ export declare enum GracefulShutdownErrorCode {
3
+ SERVICE_UNAVAILABLE = "GRACEFUL_SHUTDOWN_SERVICE_UNAVAILABLE",
4
+ SHUTDOWN_TIMEOUT = "GRACEFUL_SHUTDOWN_TIMEOUT",
5
+ HTTP_SERVER_CLOSE_ERROR = "GRACEFUL_SHUTDOWN_HTTP_SERVER_CLOSE_ERROR",
6
+ DATABASE_CLOSE_ERROR = "GRACEFUL_SHUTDOWN_DATABASE_CLOSE_ERROR",
7
+ RABBITMQ_CLOSE_ERROR = "GRACEFUL_SHUTDOWN_RABBITMQ_CLOSE_ERROR",
8
+ REDIS_CLOSE_ERROR = "GRACEFUL_SHUTDOWN_REDIS_CLOSE_ERROR",
9
+ CONNECTION_CLOSE_ERROR = "GRACEFUL_SHUTDOWN_CONNECTION_CLOSE_ERROR",
10
+ SHUTDOWN_ERROR = "GRACEFUL_SHUTDOWN_ERROR"
11
+ }
12
+ export declare const GracefulShutdownErrorMessage: Record<GracefulShutdownErrorCode, string>;
13
+ export interface ICloseableResource {
14
+ close: () => Promise<void> | void;
15
+ name?: string;
16
+ }
17
+ export interface IGracefulShutdownConfig {
18
+ logger: Logger;
19
+ httpServer?: {
20
+ close: (callback: (err?: Error) => void) => void;
21
+ };
22
+ databases?: ICloseableResource[];
23
+ messageQueues?: ICloseableResource[];
24
+ caches?: ICloseableResource[];
25
+ otherResources?: ICloseableResource[];
26
+ shutdownTimeout?: number;
27
+ requestsTimeout?: number;
28
+ checkInterval?: number;
29
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GracefulShutdownErrorMessage = exports.GracefulShutdownErrorCode = void 0;
4
+ var GracefulShutdownErrorCode;
5
+ (function (GracefulShutdownErrorCode) {
6
+ GracefulShutdownErrorCode["SERVICE_UNAVAILABLE"] = "GRACEFUL_SHUTDOWN_SERVICE_UNAVAILABLE";
7
+ GracefulShutdownErrorCode["SHUTDOWN_TIMEOUT"] = "GRACEFUL_SHUTDOWN_TIMEOUT";
8
+ GracefulShutdownErrorCode["HTTP_SERVER_CLOSE_ERROR"] = "GRACEFUL_SHUTDOWN_HTTP_SERVER_CLOSE_ERROR";
9
+ GracefulShutdownErrorCode["DATABASE_CLOSE_ERROR"] = "GRACEFUL_SHUTDOWN_DATABASE_CLOSE_ERROR";
10
+ GracefulShutdownErrorCode["RABBITMQ_CLOSE_ERROR"] = "GRACEFUL_SHUTDOWN_RABBITMQ_CLOSE_ERROR";
11
+ GracefulShutdownErrorCode["REDIS_CLOSE_ERROR"] = "GRACEFUL_SHUTDOWN_REDIS_CLOSE_ERROR";
12
+ GracefulShutdownErrorCode["CONNECTION_CLOSE_ERROR"] = "GRACEFUL_SHUTDOWN_CONNECTION_CLOSE_ERROR";
13
+ GracefulShutdownErrorCode["SHUTDOWN_ERROR"] = "GRACEFUL_SHUTDOWN_ERROR";
14
+ })(GracefulShutdownErrorCode || (exports.GracefulShutdownErrorCode = GracefulShutdownErrorCode = {}));
15
+ exports.GracefulShutdownErrorMessage = {
16
+ [GracefulShutdownErrorCode.SERVICE_UNAVAILABLE]: 'Server is shutting down, please try again later',
17
+ [GracefulShutdownErrorCode.SHUTDOWN_TIMEOUT]: 'Graceful shutdown timeout, forcing exit',
18
+ [GracefulShutdownErrorCode.HTTP_SERVER_CLOSE_ERROR]: 'Error closing HTTP server',
19
+ [GracefulShutdownErrorCode.DATABASE_CLOSE_ERROR]: 'Error closing database connection',
20
+ [GracefulShutdownErrorCode.RABBITMQ_CLOSE_ERROR]: 'Error closing RabbitMQ connection',
21
+ [GracefulShutdownErrorCode.REDIS_CLOSE_ERROR]: 'Error closing Redis connections',
22
+ [GracefulShutdownErrorCode.CONNECTION_CLOSE_ERROR]: 'Error closing connection',
23
+ [GracefulShutdownErrorCode.SHUTDOWN_ERROR]: 'Error during graceful shutdown',
24
+ };
@@ -1,5 +1,6 @@
1
1
  export * from './interface';
2
2
  export * from './enum';
3
+ export * from './graceful-shutdown.types';
3
4
  export type IMonitorTimerLocals = {
4
5
  start: Date;
5
6
  startTime: [number, number];
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./interface"), exports);
18
18
  __exportStar(require("./enum"), exports);
19
+ __exportStar(require("./graceful-shutdown.types"), exports);
@@ -1,7 +1,6 @@
1
1
  export interface CreateFuturePartitionsInfrastructureResponse {
2
2
  data: {
3
3
  message: string;
4
- partitionsCreated: number;
5
4
  requestId: string;
6
5
  };
7
6
  }
@@ -0,0 +1,2 @@
1
+ import { IGracefulShutdownConfig } from '../types/graceful-shutdown.types';
2
+ export declare const setupGracefulShutdown: (config: IGracefulShutdownConfig) => void;
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupGracefulShutdown = void 0;
4
+ const request_tracker_utils_1 = require("../utils/request-tracker.utils");
5
+ const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
6
+ const types_1 = require("../types");
7
+ const DEFAULT_SHUTDOWN_TIMEOUT = 30000;
8
+ const DEFAULT_REQUESTS_TIMEOUT = 25000;
9
+ const DEFAULT_CHECK_INTERVAL = 100;
10
+ const createGracefulShutdownLogger = (baseLogger) => {
11
+ const defaultPayload = {
12
+ context: 'GracefulShutdown',
13
+ scenario: types_1.LogScenario.SYSTEM_STARTUP,
14
+ };
15
+ const wrappedLogger = Object.create(baseLogger);
16
+ wrappedLogger.log = (message, payload, options) => baseLogger.log(message, { ...defaultPayload, ...payload }, options);
17
+ wrappedLogger.info = (message, payload, options) => baseLogger.info(message, { ...defaultPayload, ...payload }, options);
18
+ wrappedLogger.debug = (message, payload, options) => baseLogger.debug(message, { ...defaultPayload, ...payload }, options);
19
+ wrappedLogger.warn = (message, payload, options) => baseLogger.warn(message, { ...defaultPayload, ...payload }, options);
20
+ wrappedLogger.error = (message, payload, options) => baseLogger.error(message, { ...defaultPayload, ...payload }, options);
21
+ return wrappedLogger;
22
+ };
23
+ const setupGracefulShutdown = (config) => {
24
+ const { logger: baseLogger, httpServer, databases = [], messageQueues = [], caches = [], otherResources = [], shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT, requestsTimeout = DEFAULT_REQUESTS_TIMEOUT, checkInterval = DEFAULT_CHECK_INTERVAL, } = config;
25
+ const logger = createGracefulShutdownLogger(baseLogger);
26
+ const gracefulShutdown = async (signal) => {
27
+ if ((0, request_tracker_utils_1.getIsShuttingDown)()) {
28
+ logger.warn(`Already shutting down, ignoring ${signal}`);
29
+ return;
30
+ }
31
+ (0, request_tracker_utils_1.setIsShuttingDown)(true);
32
+ logger.log(`Received ${signal}, starting graceful shutdown...`, {
33
+ data: {
34
+ activeRequests: (0, request_tracker_utils_1.getActiveRequestsCount)(),
35
+ },
36
+ });
37
+ const forceShutdownTimer = setTimeout(() => {
38
+ logger.error(`Graceful shutdown timeout after ${shutdownTimeout / 1000}s, forcing exit`, {
39
+ data: {
40
+ activeRequests: (0, request_tracker_utils_1.getActiveRequestsCount)(),
41
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.SHUTDOWN_TIMEOUT,
42
+ },
43
+ });
44
+ process.exit(1);
45
+ }, shutdownTimeout);
46
+ try {
47
+ const activeRequests = (0, request_tracker_utils_1.getActiveRequestsCount)();
48
+ if (activeRequests > 0) {
49
+ logger.log(`Waiting for ${activeRequests} active requests to complete...`, {
50
+ data: {
51
+ activeRequests,
52
+ timeoutMs: requestsTimeout,
53
+ },
54
+ });
55
+ try {
56
+ await (0, request_tracker_utils_1.waitForActiveRequests)(requestsTimeout, checkInterval, logger);
57
+ }
58
+ catch (waitError) {
59
+ logger.warn('Some requests did not complete in time, proceeding with shutdown', {
60
+ data: {
61
+ activeRequests: (0, request_tracker_utils_1.getActiveRequestsCount)(),
62
+ },
63
+ });
64
+ }
65
+ }
66
+ if (httpServer) {
67
+ await new Promise((resolve, reject) => {
68
+ httpServer.close((err) => {
69
+ if (err) {
70
+ logger.error('Error closing HTTP server', {
71
+ data: {
72
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.HTTP_SERVER_CLOSE_ERROR,
73
+ },
74
+ });
75
+ reject(err);
76
+ }
77
+ else {
78
+ logger.log('HTTP server closed, no longer accepting new connections');
79
+ resolve();
80
+ }
81
+ });
82
+ });
83
+ }
84
+ if (messageQueues.length > 0) {
85
+ await Promise.allSettled(messageQueues.map(async (mq) => {
86
+ try {
87
+ await mq.close();
88
+ logger.log(`Message queue connection closed${mq.name ? `: ${mq.name}` : ''}`);
89
+ }
90
+ catch (error) {
91
+ logger.error(`Error closing message queue connection${mq.name ? `: ${mq.name}` : ''}`, {
92
+ data: {
93
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.RABBITMQ_CLOSE_ERROR,
94
+ error: error instanceof Error ? error.message : String(error),
95
+ },
96
+ });
97
+ }
98
+ }));
99
+ }
100
+ if (caches.length > 0) {
101
+ await Promise.allSettled(caches.map(async (cache) => {
102
+ try {
103
+ await cache.close();
104
+ logger.log(`Cache connection closed${cache.name ? `: ${cache.name}` : ''}`);
105
+ }
106
+ catch (error) {
107
+ logger.error(`Error closing cache connection${cache.name ? `: ${cache.name}` : ''}`, {
108
+ data: {
109
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.REDIS_CLOSE_ERROR,
110
+ error: error instanceof Error ? error.message : String(error),
111
+ },
112
+ });
113
+ }
114
+ }));
115
+ }
116
+ if (databases.length > 0) {
117
+ await Promise.allSettled(databases.map(async (db) => {
118
+ try {
119
+ await db.close();
120
+ logger.log(`Database connection closed${db.name ? `: ${db.name}` : ''}`);
121
+ }
122
+ catch (error) {
123
+ logger.error(`Error closing database connection${db.name ? `: ${db.name}` : ''}`, {
124
+ data: {
125
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.DATABASE_CLOSE_ERROR,
126
+ error: error instanceof Error ? error.message : String(error),
127
+ },
128
+ });
129
+ }
130
+ }));
131
+ }
132
+ if (otherResources.length > 0) {
133
+ await Promise.allSettled(otherResources.map(async (resource) => {
134
+ try {
135
+ await resource.close();
136
+ logger.log(`Resource closed${resource.name ? `: ${resource.name}` : ''}`);
137
+ }
138
+ catch (error) {
139
+ logger.error(`Error closing resource${resource.name ? `: ${resource.name}` : ''}`, {
140
+ data: {
141
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.CONNECTION_CLOSE_ERROR,
142
+ error: error instanceof Error ? error.message : String(error),
143
+ },
144
+ });
145
+ }
146
+ }));
147
+ }
148
+ logger.log('Graceful shutdown completed successfully');
149
+ clearTimeout(forceShutdownTimer);
150
+ process.exit(0);
151
+ }
152
+ catch (shutdownError) {
153
+ logger.error('Error during graceful shutdown', {
154
+ data: {
155
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.SHUTDOWN_ERROR,
156
+ error: shutdownError instanceof Error ? shutdownError.message : String(shutdownError),
157
+ },
158
+ });
159
+ clearTimeout(forceShutdownTimer);
160
+ process.exit(1);
161
+ }
162
+ };
163
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
164
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
165
+ process.on('uncaughtException', (err) => {
166
+ logger.error('Uncaught Exception', {
167
+ data: {
168
+ error: err.message,
169
+ stack: err.stack,
170
+ },
171
+ });
172
+ gracefulShutdown('uncaughtException');
173
+ });
174
+ process.on('unhandledRejection', (reason) => {
175
+ logger.error('Unhandled Rejection', {
176
+ data: {
177
+ reason: reason instanceof Error ? reason.message : String(reason),
178
+ },
179
+ });
180
+ gracefulShutdown('unhandledRejection');
181
+ });
182
+ logger.log('Graceful shutdown handlers registered');
183
+ };
184
+ exports.setupGracefulShutdown = setupGracefulShutdown;
@@ -3,3 +3,5 @@ export * from './discord-webhook-log-formatter.utils';
3
3
  export * from './json.utils';
4
4
  export * from './alert.utils';
5
5
  export * from './non-blocking-promise.utils';
6
+ export * from './request-tracker.utils';
7
+ export * from './graceful-shutdown.utils';
@@ -19,3 +19,5 @@ __exportStar(require("./discord-webhook-log-formatter.utils"), exports);
19
19
  __exportStar(require("./json.utils"), exports);
20
20
  __exportStar(require("./alert.utils"), exports);
21
21
  __exportStar(require("./non-blocking-promise.utils"), exports);
22
+ __exportStar(require("./request-tracker.utils"), exports);
23
+ __exportStar(require("./graceful-shutdown.utils"), exports);
@@ -0,0 +1,7 @@
1
+ import { Logger } from '../models/logger.model';
2
+ export declare const getActiveRequestsCount: () => number;
3
+ export declare const getIsShuttingDown: () => boolean;
4
+ export declare const setIsShuttingDown: (value: boolean) => void;
5
+ export declare const incrementActiveRequests: () => void;
6
+ export declare const decrementActiveRequests: () => void;
7
+ export declare const waitForActiveRequests: (timeout?: number, checkInterval?: number, logger?: Logger) => Promise<void>;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitForActiveRequests = exports.decrementActiveRequests = exports.incrementActiveRequests = exports.setIsShuttingDown = exports.getIsShuttingDown = exports.getActiveRequestsCount = void 0;
4
+ const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
5
+ let activeRequests = 0;
6
+ let isShuttingDown = false;
7
+ const getActiveRequestsCount = () => activeRequests;
8
+ exports.getActiveRequestsCount = getActiveRequestsCount;
9
+ const getIsShuttingDown = () => isShuttingDown;
10
+ exports.getIsShuttingDown = getIsShuttingDown;
11
+ const setIsShuttingDown = (value) => {
12
+ isShuttingDown = value;
13
+ };
14
+ exports.setIsShuttingDown = setIsShuttingDown;
15
+ const incrementActiveRequests = () => {
16
+ activeRequests++;
17
+ };
18
+ exports.incrementActiveRequests = incrementActiveRequests;
19
+ const decrementActiveRequests = () => {
20
+ activeRequests--;
21
+ };
22
+ exports.decrementActiveRequests = decrementActiveRequests;
23
+ const waitForActiveRequests = async (timeout = 30000, checkInterval = 100, logger) => {
24
+ const startTime = Date.now();
25
+ return new Promise((resolve, reject) => {
26
+ const check = () => {
27
+ if (activeRequests === 0) {
28
+ logger === null || logger === void 0 ? void 0 : logger.log('All active requests completed');
29
+ resolve();
30
+ return;
31
+ }
32
+ if (Date.now() - startTime >= timeout) {
33
+ const errorMessage = `Timeout waiting for ${activeRequests} active requests to complete`;
34
+ logger === null || logger === void 0 ? void 0 : logger.warn(errorMessage, {
35
+ data: {
36
+ activeRequests,
37
+ timeoutMs: timeout,
38
+ errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.SHUTDOWN_TIMEOUT,
39
+ },
40
+ });
41
+ reject(new Error(errorMessage));
42
+ return;
43
+ }
44
+ logger === null || logger === void 0 ? void 0 : logger.debug(`Waiting for ${activeRequests} active requests to complete...`, {
45
+ data: {
46
+ activeRequests,
47
+ elapsedMs: Date.now() - startTime,
48
+ },
49
+ });
50
+ setTimeout(check, checkInterval);
51
+ };
52
+ check();
53
+ });
54
+ };
55
+ exports.waitForActiveRequests = waitForActiveRequests;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abyss-project/monitor",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "description": "Core package to interact with Abyss-Monitor",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",