@abyss-project/monitor 1.0.56 → 1.0.58
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/frameworks/express/decorators.d.ts +1 -0
- package/dist/frameworks/express/decorators.js +7 -1
- package/dist/frameworks/express/metadata.d.ts +1 -0
- package/dist/frameworks/express/metadata.js +2 -1
- package/dist/middlewares/request-tracker.middleware.d.ts +6 -1
- package/dist/middlewares/request-tracker.middleware.js +6 -3
- package/dist/utils/graceful-shutdown.utils.js +29 -10
- package/dist/utils/killable-route.utils.d.ts +3 -0
- package/dist/utils/killable-route.utils.js +26 -0
- package/dist/utils/request-tracker.utils.d.ts +4 -2
- package/dist/utils/request-tracker.utils.js +21 -6
- package/package.json +1 -1
|
@@ -49,4 +49,5 @@ export declare function Middlewares(middlewares: Middleware[]): any;
|
|
|
49
49
|
export declare function Body(schema: Joi.Schema, options?: ValidationOptions): any;
|
|
50
50
|
export declare function Params(schema: Joi.Schema, options?: ValidationOptions): any;
|
|
51
51
|
export declare function Query(schema: Joi.Schema, options?: ValidationOptions): any;
|
|
52
|
+
export declare function Killable(): any;
|
|
52
53
|
export {};
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Query = exports.Params = exports.Body = exports.Middlewares = exports.Middleware = exports.Controller = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = void 0;
|
|
6
|
+
exports.Killable = exports.Query = exports.Params = exports.Body = exports.Middlewares = exports.Middleware = exports.Controller = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = void 0;
|
|
7
7
|
const types_1 = require("../../types");
|
|
8
8
|
const _1 = require(".");
|
|
9
9
|
const joi_to_swagger_1 = __importDefault(require("joi-to-swagger"));
|
|
@@ -161,3 +161,9 @@ function Query(schema, options) {
|
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
exports.Query = Query;
|
|
164
|
+
function Killable() {
|
|
165
|
+
return function (target, propertyKey, descriptor) {
|
|
166
|
+
Reflect.defineMetadata(_1.KILLABLE_WATERMARK, true, target, propertyKey);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
exports.Killable = Killable;
|
|
@@ -7,3 +7,4 @@ export declare const ROUTE_WATERMARK = "__route__";
|
|
|
7
7
|
export declare const DOCS_WATERMARK = "__docs__";
|
|
8
8
|
export declare const MIDDLEWARE_WATERMARK = "__middleware__";
|
|
9
9
|
export declare const MIDDLEWARE_HOOK_WATERMARK = "__middleware_hook__";
|
|
10
|
+
export declare const KILLABLE_WATERMARK = "__killable__";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MIDDLEWARE_HOOK_WATERMARK = exports.MIDDLEWARE_WATERMARK = exports.DOCS_WATERMARK = exports.ROUTE_WATERMARK = exports.OPTIONS_WATERMARK = exports.METHOD_WATERMARK = exports.PATH_WATERMARK = exports.MASKED_WATERMARK = exports.CONTROLLER_WATERMARK = void 0;
|
|
3
|
+
exports.KILLABLE_WATERMARK = exports.MIDDLEWARE_HOOK_WATERMARK = exports.MIDDLEWARE_WATERMARK = exports.DOCS_WATERMARK = exports.ROUTE_WATERMARK = exports.OPTIONS_WATERMARK = exports.METHOD_WATERMARK = exports.PATH_WATERMARK = exports.MASKED_WATERMARK = exports.CONTROLLER_WATERMARK = void 0;
|
|
4
4
|
exports.CONTROLLER_WATERMARK = '__controller__';
|
|
5
5
|
exports.MASKED_WATERMARK = '__masked__';
|
|
6
6
|
exports.PATH_WATERMARK = '__path__';
|
|
@@ -10,3 +10,4 @@ exports.ROUTE_WATERMARK = '__route__';
|
|
|
10
10
|
exports.DOCS_WATERMARK = '__docs__';
|
|
11
11
|
exports.MIDDLEWARE_WATERMARK = '__middleware__';
|
|
12
12
|
exports.MIDDLEWARE_HOOK_WATERMARK = '__middleware_hook__';
|
|
13
|
+
exports.KILLABLE_WATERMARK = '__killable__';
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express-serve-static-core';
|
|
2
2
|
import { Logger } from '../models/logger.model';
|
|
3
|
-
export
|
|
3
|
+
export type IsKillableFn = (req: Request) => boolean;
|
|
4
|
+
export interface RequestTrackerOptions {
|
|
5
|
+
logger?: Logger;
|
|
6
|
+
isKillable?: IsKillableFn;
|
|
7
|
+
}
|
|
8
|
+
export declare const createRequestTrackerMiddleware: (options?: RequestTrackerOptions) => (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -3,8 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createRequestTrackerMiddleware = void 0;
|
|
4
4
|
const request_tracker_utils_1 = require("../utils/request-tracker.utils");
|
|
5
5
|
const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
|
|
6
|
-
const
|
|
6
|
+
const killable_route_utils_1 = require("../utils/killable-route.utils");
|
|
7
|
+
const createRequestTrackerMiddleware = (options) => {
|
|
8
|
+
const { logger, isKillable } = options || {};
|
|
7
9
|
return (req, res, next) => {
|
|
10
|
+
const requestIsKillable = isKillable ? isKillable(req) : (0, killable_route_utils_1.isRouteKillable)(req);
|
|
8
11
|
if ((0, request_tracker_utils_1.getIsShuttingDown)()) {
|
|
9
12
|
res.status(503).json({
|
|
10
13
|
errorCode: graceful_shutdown_types_1.GracefulShutdownErrorCode.SERVICE_UNAVAILABLE,
|
|
@@ -12,13 +15,13 @@ const createRequestTrackerMiddleware = (logger) => {
|
|
|
12
15
|
});
|
|
13
16
|
return;
|
|
14
17
|
}
|
|
15
|
-
(0, request_tracker_utils_1.incrementActiveRequests)();
|
|
18
|
+
(0, request_tracker_utils_1.incrementActiveRequests)(requestIsKillable);
|
|
16
19
|
let cleaned = false;
|
|
17
20
|
const cleanup = () => {
|
|
18
21
|
if (cleaned)
|
|
19
22
|
return;
|
|
20
23
|
cleaned = true;
|
|
21
|
-
(0, request_tracker_utils_1.decrementActiveRequests)();
|
|
24
|
+
(0, request_tracker_utils_1.decrementActiveRequests)(requestIsKillable);
|
|
22
25
|
};
|
|
23
26
|
res.on('finish', cleanup);
|
|
24
27
|
res.on('close', cleanup);
|
|
@@ -3,20 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.setupGracefulShutdown = void 0;
|
|
4
4
|
const request_tracker_utils_1 = require("../utils/request-tracker.utils");
|
|
5
5
|
const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
|
|
6
|
+
const types_1 = require("../types");
|
|
6
7
|
const DEFAULT_SHUTDOWN_TIMEOUT = 30000;
|
|
7
8
|
const DEFAULT_REQUESTS_TIMEOUT = 25000;
|
|
8
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
|
+
};
|
|
9
23
|
const setupGracefulShutdown = (config) => {
|
|
10
|
-
const { logger, httpServer, databases = [], messageQueues = [], caches = [], otherResources = [], shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT, requestsTimeout = DEFAULT_REQUESTS_TIMEOUT, checkInterval = DEFAULT_CHECK_INTERVAL, } = 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);
|
|
11
26
|
const gracefulShutdown = async (signal) => {
|
|
12
27
|
if ((0, request_tracker_utils_1.getIsShuttingDown)()) {
|
|
13
28
|
logger.warn(`Already shutting down, ignoring ${signal}`);
|
|
14
29
|
return;
|
|
15
30
|
}
|
|
16
31
|
(0, request_tracker_utils_1.setIsShuttingDown)(true);
|
|
17
|
-
|
|
32
|
+
const totalRequests = (0, request_tracker_utils_1.getTotalRequestsCount)();
|
|
33
|
+
const killableRequests = (0, request_tracker_utils_1.getKillableRequestsCount)();
|
|
34
|
+
logger.log(`Received ${signal}, starting graceful shutdown...`, {
|
|
18
35
|
data: {
|
|
19
36
|
activeRequests: (0, request_tracker_utils_1.getActiveRequestsCount)(),
|
|
37
|
+
killableRequests,
|
|
38
|
+
totalRequests,
|
|
20
39
|
},
|
|
21
40
|
});
|
|
22
41
|
const forceShutdownTimer = setTimeout(() => {
|
|
@@ -31,7 +50,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
31
50
|
try {
|
|
32
51
|
const activeRequests = (0, request_tracker_utils_1.getActiveRequestsCount)();
|
|
33
52
|
if (activeRequests > 0) {
|
|
34
|
-
logger.
|
|
53
|
+
logger.log(`Waiting for ${activeRequests} active requests to complete...`, {
|
|
35
54
|
data: {
|
|
36
55
|
activeRequests,
|
|
37
56
|
timeoutMs: requestsTimeout,
|
|
@@ -60,7 +79,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
60
79
|
reject(err);
|
|
61
80
|
}
|
|
62
81
|
else {
|
|
63
|
-
logger.
|
|
82
|
+
logger.log('HTTP server closed, no longer accepting new connections');
|
|
64
83
|
resolve();
|
|
65
84
|
}
|
|
66
85
|
});
|
|
@@ -70,7 +89,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
70
89
|
await Promise.allSettled(messageQueues.map(async (mq) => {
|
|
71
90
|
try {
|
|
72
91
|
await mq.close();
|
|
73
|
-
logger.
|
|
92
|
+
logger.log(`Message queue connection closed${mq.name ? `: ${mq.name}` : ''}`);
|
|
74
93
|
}
|
|
75
94
|
catch (error) {
|
|
76
95
|
logger.error(`Error closing message queue connection${mq.name ? `: ${mq.name}` : ''}`, {
|
|
@@ -86,7 +105,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
86
105
|
await Promise.allSettled(caches.map(async (cache) => {
|
|
87
106
|
try {
|
|
88
107
|
await cache.close();
|
|
89
|
-
logger.
|
|
108
|
+
logger.log(`Cache connection closed${cache.name ? `: ${cache.name}` : ''}`);
|
|
90
109
|
}
|
|
91
110
|
catch (error) {
|
|
92
111
|
logger.error(`Error closing cache connection${cache.name ? `: ${cache.name}` : ''}`, {
|
|
@@ -102,7 +121,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
102
121
|
await Promise.allSettled(databases.map(async (db) => {
|
|
103
122
|
try {
|
|
104
123
|
await db.close();
|
|
105
|
-
logger.
|
|
124
|
+
logger.log(`Database connection closed${db.name ? `: ${db.name}` : ''}`);
|
|
106
125
|
}
|
|
107
126
|
catch (error) {
|
|
108
127
|
logger.error(`Error closing database connection${db.name ? `: ${db.name}` : ''}`, {
|
|
@@ -118,7 +137,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
118
137
|
await Promise.allSettled(otherResources.map(async (resource) => {
|
|
119
138
|
try {
|
|
120
139
|
await resource.close();
|
|
121
|
-
logger.
|
|
140
|
+
logger.log(`Resource closed${resource.name ? `: ${resource.name}` : ''}`);
|
|
122
141
|
}
|
|
123
142
|
catch (error) {
|
|
124
143
|
logger.error(`Error closing resource${resource.name ? `: ${resource.name}` : ''}`, {
|
|
@@ -130,7 +149,7 @@ const setupGracefulShutdown = (config) => {
|
|
|
130
149
|
}
|
|
131
150
|
}));
|
|
132
151
|
}
|
|
133
|
-
logger.
|
|
152
|
+
logger.log('Graceful shutdown completed successfully');
|
|
134
153
|
clearTimeout(forceShutdownTimer);
|
|
135
154
|
process.exit(0);
|
|
136
155
|
}
|
|
@@ -164,6 +183,6 @@ const setupGracefulShutdown = (config) => {
|
|
|
164
183
|
});
|
|
165
184
|
gracefulShutdown('unhandledRejection');
|
|
166
185
|
});
|
|
167
|
-
logger.
|
|
186
|
+
logger.log('Graceful shutdown handlers registered');
|
|
168
187
|
};
|
|
169
188
|
exports.setupGracefulShutdown = setupGracefulShutdown;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRouteKillable = void 0;
|
|
4
|
+
require("reflect-metadata");
|
|
5
|
+
const metadata_1 = require("../frameworks/express/metadata");
|
|
6
|
+
const isRouteKillable = (req) => {
|
|
7
|
+
const route = req.route;
|
|
8
|
+
if (!route || !route.stack || route.stack.length === 0) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
for (const layer of route.stack) {
|
|
12
|
+
const handler = layer.handle;
|
|
13
|
+
if (!handler)
|
|
14
|
+
continue;
|
|
15
|
+
const prototype = Object.getPrototypeOf(handler);
|
|
16
|
+
if (prototype && prototype.constructor) {
|
|
17
|
+
const propertyKey = handler.name;
|
|
18
|
+
const isKillable = Reflect.getMetadata(metadata_1.KILLABLE_WATERMARK, prototype.constructor.prototype, propertyKey);
|
|
19
|
+
if (isKillable) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
};
|
|
26
|
+
exports.isRouteKillable = isRouteKillable;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Logger } from '../models/logger.model';
|
|
2
2
|
export declare const getActiveRequestsCount: () => number;
|
|
3
|
+
export declare const getKillableRequestsCount: () => number;
|
|
4
|
+
export declare const getTotalRequestsCount: () => number;
|
|
3
5
|
export declare const getIsShuttingDown: () => boolean;
|
|
4
6
|
export declare const setIsShuttingDown: (value: boolean) => void;
|
|
5
|
-
export declare const incrementActiveRequests: () => void;
|
|
6
|
-
export declare const decrementActiveRequests: () => void;
|
|
7
|
+
export declare const incrementActiveRequests: (isKillable?: boolean) => void;
|
|
8
|
+
export declare const decrementActiveRequests: (isKillable?: boolean) => void;
|
|
7
9
|
export declare const waitForActiveRequests: (timeout?: number, checkInterval?: number, logger?: Logger) => Promise<void>;
|
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.waitForActiveRequests = exports.decrementActiveRequests = exports.incrementActiveRequests = exports.setIsShuttingDown = exports.getIsShuttingDown = exports.getActiveRequestsCount = void 0;
|
|
3
|
+
exports.waitForActiveRequests = exports.decrementActiveRequests = exports.incrementActiveRequests = exports.setIsShuttingDown = exports.getIsShuttingDown = exports.getTotalRequestsCount = exports.getKillableRequestsCount = exports.getActiveRequestsCount = void 0;
|
|
4
4
|
const graceful_shutdown_types_1 = require("../types/graceful-shutdown.types");
|
|
5
5
|
let activeRequests = 0;
|
|
6
|
+
let killableRequests = 0;
|
|
6
7
|
let isShuttingDown = false;
|
|
7
8
|
const getActiveRequestsCount = () => activeRequests;
|
|
8
9
|
exports.getActiveRequestsCount = getActiveRequestsCount;
|
|
10
|
+
const getKillableRequestsCount = () => killableRequests;
|
|
11
|
+
exports.getKillableRequestsCount = getKillableRequestsCount;
|
|
12
|
+
const getTotalRequestsCount = () => activeRequests + killableRequests;
|
|
13
|
+
exports.getTotalRequestsCount = getTotalRequestsCount;
|
|
9
14
|
const getIsShuttingDown = () => isShuttingDown;
|
|
10
15
|
exports.getIsShuttingDown = getIsShuttingDown;
|
|
11
16
|
const setIsShuttingDown = (value) => {
|
|
12
17
|
isShuttingDown = value;
|
|
13
18
|
};
|
|
14
19
|
exports.setIsShuttingDown = setIsShuttingDown;
|
|
15
|
-
const incrementActiveRequests = () => {
|
|
16
|
-
|
|
20
|
+
const incrementActiveRequests = (isKillable = false) => {
|
|
21
|
+
if (isKillable) {
|
|
22
|
+
killableRequests++;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
activeRequests++;
|
|
26
|
+
}
|
|
17
27
|
};
|
|
18
28
|
exports.incrementActiveRequests = incrementActiveRequests;
|
|
19
|
-
const decrementActiveRequests = () => {
|
|
20
|
-
|
|
29
|
+
const decrementActiveRequests = (isKillable = false) => {
|
|
30
|
+
if (isKillable) {
|
|
31
|
+
killableRequests--;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
activeRequests--;
|
|
35
|
+
}
|
|
21
36
|
};
|
|
22
37
|
exports.decrementActiveRequests = decrementActiveRequests;
|
|
23
38
|
const waitForActiveRequests = async (timeout = 30000, checkInterval = 100, logger) => {
|
|
@@ -25,7 +40,7 @@ const waitForActiveRequests = async (timeout = 30000, checkInterval = 100, logge
|
|
|
25
40
|
return new Promise((resolve, reject) => {
|
|
26
41
|
const check = () => {
|
|
27
42
|
if (activeRequests === 0) {
|
|
28
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
43
|
+
logger === null || logger === void 0 ? void 0 : logger.log('All active requests completed');
|
|
29
44
|
resolve();
|
|
30
45
|
return;
|
|
31
46
|
}
|