@scpxl/nodejs-framework 1.0.17 → 1.0.19
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/README.md +29 -0
- package/dist/api-requester/api-requester.d.ts +27 -6
- package/dist/api-requester/api-requester.d.ts.map +1 -1
- package/dist/api-requester/api-requester.js +188 -26
- package/dist/api-requester/api-requester.js.map +2 -2
- package/dist/api-requester/index.d.ts +1 -0
- package/dist/api-requester/index.d.ts.map +1 -1
- package/dist/api-requester/index.js.map +1 -1
- package/dist/application/base-application.d.ts +15 -22
- package/dist/application/base-application.d.ts.map +1 -1
- package/dist/application/base-application.js +145 -111
- package/dist/application/base-application.js.map +2 -2
- package/dist/application/command-application.d.ts.map +1 -1
- package/dist/application/command-application.js +3 -4
- package/dist/application/command-application.js.map +2 -2
- package/dist/application/web-application.d.ts.map +1 -1
- package/dist/application/web-application.js +9 -2
- package/dist/application/web-application.js.map +2 -2
- package/dist/cache/manager.d.ts +87 -6
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +77 -30
- package/dist/cache/manager.js.map +2 -2
- package/dist/cluster/cluster-manager.d.ts.map +1 -1
- package/dist/cluster/cluster-manager.js +5 -8
- package/dist/cluster/cluster-manager.js.map +2 -2
- package/dist/config/env.d.ts +11 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +103 -0
- package/dist/config/env.js.map +7 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +7 -0
- package/dist/config/schema.d.ts +408 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +218 -0
- package/dist/config/schema.js.map +7 -0
- package/dist/database/dynamic-entity.d.ts.map +1 -1
- package/dist/database/dynamic-entity.js +6 -2
- package/dist/database/dynamic-entity.js.map +2 -2
- package/dist/database/instance.d.ts.map +1 -1
- package/dist/database/instance.js +0 -8
- package/dist/database/instance.js.map +2 -2
- package/dist/database/manager.d.ts.map +1 -1
- package/dist/database/manager.js +71 -9
- package/dist/database/manager.js.map +2 -2
- package/dist/error/error-reporter.d.ts +96 -0
- package/dist/error/error-reporter.d.ts.map +1 -0
- package/dist/error/error-reporter.js +228 -0
- package/dist/error/error-reporter.js.map +7 -0
- package/dist/error/error.interface.d.ts +126 -0
- package/dist/error/error.interface.d.ts.map +1 -0
- package/dist/error/error.interface.js +45 -0
- package/dist/error/error.interface.js.map +7 -0
- package/dist/error/framework-errors.d.ts +113 -0
- package/dist/error/framework-errors.d.ts.map +1 -0
- package/dist/error/framework-errors.js +176 -0
- package/dist/error/framework-errors.js.map +7 -0
- package/dist/error/index.d.ts +6 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +34 -0
- package/dist/error/index.js.map +7 -0
- package/dist/event/manager.d.ts.map +1 -1
- package/dist/event/manager.js +2 -9
- package/dist/event/manager.js.map +2 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +2 -2
- package/dist/lifecycle/exit.d.ts +2 -1
- package/dist/lifecycle/exit.d.ts.map +1 -1
- package/dist/lifecycle/exit.js +6 -0
- package/dist/lifecycle/exit.js.map +2 -2
- package/dist/lifecycle/index.d.ts +7 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +12 -0
- package/dist/lifecycle/index.js.map +7 -0
- package/dist/lifecycle/lifecycle-manager.d.ts +55 -2
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
- package/dist/lifecycle/lifecycle-manager.js +233 -7
- package/dist/lifecycle/lifecycle-manager.js.map +3 -3
- package/dist/lifecycle/shutdown-controller.d.ts +15 -0
- package/dist/lifecycle/shutdown-controller.d.ts.map +1 -0
- package/dist/lifecycle/shutdown-controller.js +38 -0
- package/dist/lifecycle/shutdown-controller.js.map +7 -0
- package/dist/lifecycle/types.d.ts +28 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/lifecycle/types.js +13 -0
- package/dist/lifecycle/types.js.map +7 -0
- package/dist/logger/logger.d.ts +2 -1
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +35 -11
- package/dist/logger/logger.js.map +2 -2
- package/dist/performance/cache-performance.d.ts +6 -0
- package/dist/performance/cache-performance.d.ts.map +1 -1
- package/dist/performance/cache-performance.js +16 -0
- package/dist/performance/cache-performance.js.map +2 -2
- package/dist/performance/index.d.ts +1 -0
- package/dist/performance/index.d.ts.map +1 -1
- package/dist/performance/index.js +1 -0
- package/dist/performance/index.js.map +2 -2
- package/dist/performance/performance-monitor.d.ts.map +1 -1
- package/dist/performance/performance-monitor.js +47 -18
- package/dist/performance/performance-monitor.js.map +2 -2
- package/dist/performance/performance-monitor.plugin.d.ts +24 -0
- package/dist/performance/performance-monitor.plugin.d.ts.map +1 -0
- package/dist/performance/performance-monitor.plugin.js +89 -0
- package/dist/performance/performance-monitor.plugin.js.map +7 -0
- package/dist/performance/webserver-performance.js +1 -1
- package/dist/performance/webserver-performance.js.map +2 -2
- package/dist/queue/manager.d.ts.map +1 -1
- package/dist/queue/manager.js +3 -10
- package/dist/queue/manager.js.map +2 -2
- package/dist/queue/worker.d.ts.map +1 -1
- package/dist/queue/worker.js +2 -2
- package/dist/queue/worker.js.map +2 -2
- package/dist/redis/manager.d.ts.map +1 -1
- package/dist/redis/manager.js +228 -33
- package/dist/redis/manager.js.map +2 -2
- package/dist/request-context/index.d.ts +3 -0
- package/dist/request-context/index.d.ts.map +1 -0
- package/dist/request-context/index.js +25 -0
- package/dist/request-context/index.js.map +7 -0
- package/dist/request-context/request-context.d.ts +108 -0
- package/dist/request-context/request-context.d.ts.map +1 -0
- package/dist/request-context/request-context.interface.d.ts +46 -0
- package/dist/request-context/request-context.interface.d.ts.map +1 -0
- package/dist/request-context/request-context.interface.js +1 -0
- package/dist/request-context/request-context.interface.js.map +7 -0
- package/dist/request-context/request-context.js +79 -0
- package/dist/request-context/request-context.js.map +7 -0
- package/dist/services/aws/s3.js +4 -6
- package/dist/services/aws/s3.js.map +2 -2
- package/dist/util/file.d.ts +13 -0
- package/dist/util/file.d.ts.map +1 -1
- package/dist/util/file.js +46 -9
- package/dist/util/file.js.map +2 -2
- package/dist/util/helper.d.ts +16 -1
- package/dist/util/helper.d.ts.map +1 -1
- package/dist/util/helper.js +19 -43
- package/dist/util/helper.js.map +2 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +18 -16
- package/dist/util/index.js.map +2 -2
- package/dist/util/loader.d.ts.map +1 -1
- package/dist/util/loader.js +13 -2
- package/dist/util/loader.js.map +2 -2
- package/dist/util/os.d.ts.map +1 -1
- package/dist/util/os.js +8 -14
- package/dist/util/os.js.map +2 -2
- package/dist/util/time.d.ts +8 -2
- package/dist/util/time.d.ts.map +1 -1
- package/dist/util/time.js +12 -7
- package/dist/util/time.js.map +2 -2
- package/dist/util/timing.d.ts +36 -0
- package/dist/util/timing.d.ts.map +1 -0
- package/dist/util/timing.interface.d.ts +47 -0
- package/dist/util/timing.interface.d.ts.map +1 -0
- package/dist/util/timing.interface.js +1 -0
- package/dist/util/timing.interface.js.map +7 -0
- package/dist/util/timing.js +98 -0
- package/dist/util/timing.js.map +7 -0
- package/dist/util/url.js +1 -1
- package/dist/util/url.js.map +2 -2
- package/dist/webserver/controller/base.d.ts +3 -1
- package/dist/webserver/controller/base.d.ts.map +1 -1
- package/dist/webserver/controller/base.interface.d.ts +2 -0
- package/dist/webserver/controller/base.interface.d.ts.map +1 -1
- package/dist/webserver/controller/base.js +4 -1
- package/dist/webserver/controller/base.js.map +2 -2
- package/dist/webserver/controller/health.d.ts +8 -1
- package/dist/webserver/controller/health.d.ts.map +1 -1
- package/dist/webserver/controller/health.js +36 -22
- package/dist/webserver/controller/health.js.map +2 -2
- package/dist/webserver/webserver.d.ts +16 -2
- package/dist/webserver/webserver.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.d.ts +37 -0
- package/dist/webserver/webserver.interface.d.ts.map +1 -1
- package/dist/webserver/webserver.interface.js.map +2 -2
- package/dist/webserver/webserver.js +117 -20
- package/dist/webserver/webserver.js.map +2 -2
- package/dist/websocket/controllers/server/system.d.ts.map +1 -1
- package/dist/websocket/controllers/server/system.js.map +2 -2
- package/dist/websocket/websocket-base.d.ts.map +1 -1
- package/dist/websocket/websocket-base.js +2 -3
- package/dist/websocket/websocket-base.js.map +2 -2
- package/dist/websocket/websocket-server.d.ts +1 -1
- package/dist/websocket/websocket-server.d.ts.map +1 -1
- package/dist/websocket/websocket-server.js +7 -31
- package/dist/websocket/websocket-server.js.map +2 -2
- package/package.json +51 -10
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
import { Logger } from "../logger/index.js";
|
|
4
|
+
import { PerformanceMonitor } from "./performance-monitor.js";
|
|
5
|
+
import { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from "./index.js";
|
|
6
|
+
class PerformanceMonitorPlugin {
|
|
7
|
+
constructor(app) {
|
|
8
|
+
this.app = app;
|
|
9
|
+
}
|
|
10
|
+
static {
|
|
11
|
+
__name(this, "PerformanceMonitorPlugin");
|
|
12
|
+
}
|
|
13
|
+
started = false;
|
|
14
|
+
abortController = new AbortController();
|
|
15
|
+
/** Register and immediately start (idempotent) */
|
|
16
|
+
static register(app) {
|
|
17
|
+
const plugin = new PerformanceMonitorPlugin(app);
|
|
18
|
+
plugin.start();
|
|
19
|
+
app.lifecycle.onShutdown(async () => plugin.stop());
|
|
20
|
+
return plugin;
|
|
21
|
+
}
|
|
22
|
+
/** Initialize monitor & ancillary behaviors */
|
|
23
|
+
start() {
|
|
24
|
+
if (this.started) return;
|
|
25
|
+
const cfg = this.app["config"].performanceMonitoring;
|
|
26
|
+
if (!cfg?.enabled) {
|
|
27
|
+
Logger.debug({ message: "PerformanceMonitorPlugin: disabled via configuration" });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const options = {
|
|
31
|
+
enabled: true,
|
|
32
|
+
thresholds: cfg.thresholds,
|
|
33
|
+
maxMetricsHistory: cfg.maxMetricsHistory,
|
|
34
|
+
logSlowOperations: cfg.logSlowOperations,
|
|
35
|
+
logAllOperations: cfg.logAllOperations
|
|
36
|
+
};
|
|
37
|
+
this.app.performanceMonitor = PerformanceMonitor.initialize(options);
|
|
38
|
+
try {
|
|
39
|
+
if (cfg.monitorDatabaseOperations !== false) {
|
|
40
|
+
DatabasePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);
|
|
41
|
+
}
|
|
42
|
+
if (cfg.monitorQueueOperations !== false) {
|
|
43
|
+
QueuePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);
|
|
44
|
+
}
|
|
45
|
+
if (cfg.monitorCacheOperations !== false) {
|
|
46
|
+
CachePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
Logger.warn({ message: "PerformanceMonitorPlugin: error configuring wrappers", error });
|
|
50
|
+
}
|
|
51
|
+
if (cfg.reportInterval && cfg.reportInterval > 0) {
|
|
52
|
+
setInterval(
|
|
53
|
+
() => {
|
|
54
|
+
try {
|
|
55
|
+
const reportFormat = cfg.reportFormat ?? "detailed";
|
|
56
|
+
const report = this.app.performanceMonitor?.generateFormattedReport(reportFormat);
|
|
57
|
+
if (report) {
|
|
58
|
+
Logger.info({ message: report });
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
Logger.warn({ message: "PerformanceMonitorPlugin: failed generating report", error });
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
cfg.reportInterval,
|
|
65
|
+
{ signal: this.abortController.signal }
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
this.started = true;
|
|
69
|
+
Logger.debug({ message: "PerformanceMonitorPlugin: started" });
|
|
70
|
+
}
|
|
71
|
+
/** Destroy monitor & clear references */
|
|
72
|
+
stop() {
|
|
73
|
+
if (!this.started) return;
|
|
74
|
+
this.abortController.abort();
|
|
75
|
+
try {
|
|
76
|
+
this.app.performanceMonitor?.destroy();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
Logger.warn({ message: "PerformanceMonitorPlugin: error during destroy", error });
|
|
79
|
+
}
|
|
80
|
+
this.app.performanceMonitor = void 0;
|
|
81
|
+
this.abortController = new AbortController();
|
|
82
|
+
this.started = false;
|
|
83
|
+
Logger.debug({ message: "PerformanceMonitorPlugin: stopped" });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
PerformanceMonitorPlugin
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=performance-monitor.plugin.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/performance/performance-monitor.plugin.ts"],
|
|
4
|
+
"sourcesContent": ["import type BaseApplication from '../application/base-application.js';\nimport { Logger } from '../logger/index.js';\nimport { PerformanceMonitor, type PerformanceMonitorOptions } from './performance-monitor.js';\nimport { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from './index.js';\n\n/**\n * PerformanceMonitorPlugin\n *\n * Converts the previous side-effect constructor initialization into an opt\u2011in plugin.\n * Responsibilities:\n * - Initialize PerformanceMonitor when enabled in config\n * - Register metric wrappers (DB / Queue / Cache) based on flags\n * - Schedule periodic reporting (tracked via lifecycle for auto cleanup)\n * - Dispose monitor on shutdown\n */\nexport class PerformanceMonitorPlugin {\n private started = false;\n private abortController = new AbortController();\n\n private constructor(private readonly app: BaseApplication) {}\n\n /** Register and immediately start (idempotent) */\n public static register(app: BaseApplication): PerformanceMonitorPlugin {\n const plugin = new PerformanceMonitorPlugin(app);\n plugin.start();\n // Ensure cleanup on shutdown\n app.lifecycle.onShutdown(async () => plugin.stop());\n return plugin;\n }\n\n /** Initialize monitor & ancillary behaviors */\n public start(): void {\n if (this.started) return;\n const cfg = this.app['config'].performanceMonitoring; // internal access\n if (!cfg?.enabled) {\n Logger.debug({ message: 'PerformanceMonitorPlugin: disabled via configuration' });\n return; // remain not started\n }\n\n const options: PerformanceMonitorOptions = {\n enabled: true,\n thresholds: cfg.thresholds,\n maxMetricsHistory: cfg.maxMetricsHistory,\n logSlowOperations: cfg.logSlowOperations,\n logAllOperations: cfg.logAllOperations,\n };\n\n this.app.performanceMonitor = PerformanceMonitor.initialize(options);\n\n // Register component wrappers according to config\n try {\n if (cfg.monitorDatabaseOperations !== false) {\n DatabasePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);\n }\n if (cfg.monitorQueueOperations !== false) {\n QueuePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);\n }\n if (cfg.monitorCacheOperations !== false) {\n CachePerformanceWrapper.setPerformanceMonitor(this.app.performanceMonitor);\n }\n } catch (error) {\n Logger.warn({ message: 'PerformanceMonitorPlugin: error configuring wrappers', error });\n }\n\n // Periodic reporting\n if (cfg.reportInterval && cfg.reportInterval > 0) {\n // Note: setInterval with signal option requires Node.js 15+\n // TypeScript types may not reflect this, so we use type assertion\n (setInterval as (fn: () => void, ms: number, options?: { signal: AbortSignal }) => NodeJS.Timeout)(\n () => {\n try {\n const reportFormat = cfg.reportFormat ?? 'detailed';\n const report = this.app.performanceMonitor?.generateFormattedReport(reportFormat);\n if (report) {\n Logger.info({ message: report });\n }\n } catch (error) {\n Logger.warn({ message: 'PerformanceMonitorPlugin: failed generating report', error });\n }\n },\n cfg.reportInterval,\n { signal: this.abortController.signal },\n );\n }\n\n this.started = true;\n Logger.debug({ message: 'PerformanceMonitorPlugin: started' });\n }\n\n /** Destroy monitor & clear references */\n public stop(): void {\n if (!this.started) return;\n\n // Abort all ongoing operations (intervals, etc.)\n this.abortController.abort();\n\n try {\n this.app.performanceMonitor?.destroy();\n } catch (error) {\n Logger.warn({ message: 'PerformanceMonitorPlugin: error during destroy', error });\n }\n this.app.performanceMonitor = undefined;\n\n // Create new AbortController for potential restart\n this.abortController = new AbortController();\n\n this.started = false;\n Logger.debug({ message: 'PerformanceMonitorPlugin: stopped' });\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AACA,SAAS,cAAc;AACvB,SAAS,0BAA0D;AACnE,SAAS,yBAAyB,4BAA4B,+BAA+B;AAYtF,MAAM,yBAAyB;AAAA,EAI5B,YAA6B,KAAsB;AAAtB;AAAA,EAAuB;AAAA,EAnB9D,OAesC;AAAA;AAAA;AAAA,EAC5B,UAAU;AAAA,EACV,kBAAkB,IAAI,gBAAgB;AAAA;AAAA,EAK9C,OAAc,SAAS,KAAgD;AACrE,UAAM,SAAS,IAAI,yBAAyB,GAAG;AAC/C,WAAO,MAAM;AAEb,QAAI,UAAU,WAAW,YAAY,OAAO,KAAK,CAAC;AAClD,WAAO;AAAA,EACT;AAAA;AAAA,EAGO,QAAc;AACnB,QAAI,KAAK,QAAS;AAClB,UAAM,MAAM,KAAK,IAAI,QAAQ,EAAE;AAC/B,QAAI,CAAC,KAAK,SAAS;AACjB,aAAO,MAAM,EAAE,SAAS,uDAAuD,CAAC;AAChF;AAAA,IACF;AAEA,UAAM,UAAqC;AAAA,MACzC,SAAS;AAAA,MACT,YAAY,IAAI;AAAA,MAChB,mBAAmB,IAAI;AAAA,MACvB,mBAAmB,IAAI;AAAA,MACvB,kBAAkB,IAAI;AAAA,IACxB;AAEA,SAAK,IAAI,qBAAqB,mBAAmB,WAAW,OAAO;AAGnE,QAAI;AACF,UAAI,IAAI,8BAA8B,OAAO;AAC3C,mCAA2B,sBAAsB,KAAK,IAAI,kBAAkB;AAAA,MAC9E;AACA,UAAI,IAAI,2BAA2B,OAAO;AACxC,gCAAwB,sBAAsB,KAAK,IAAI,kBAAkB;AAAA,MAC3E;AACA,UAAI,IAAI,2BAA2B,OAAO;AACxC,gCAAwB,sBAAsB,KAAK,IAAI,kBAAkB;AAAA,MAC3E;AAAA,IACF,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,SAAS,wDAAwD,MAAM,CAAC;AAAA,IACxF;AAGA,QAAI,IAAI,kBAAkB,IAAI,iBAAiB,GAAG;AAGhD,MAAC;AAAA,QACC,MAAM;AACJ,cAAI;AACF,kBAAM,eAAe,IAAI,gBAAgB;AACzC,kBAAM,SAAS,KAAK,IAAI,oBAAoB,wBAAwB,YAAY;AAChF,gBAAI,QAAQ;AACV,qBAAO,KAAK,EAAE,SAAS,OAAO,CAAC;AAAA,YACjC;AAAA,UACF,SAAS,OAAO;AACd,mBAAO,KAAK,EAAE,SAAS,sDAAsD,MAAM,CAAC;AAAA,UACtF;AAAA,QACF;AAAA,QACA,IAAI;AAAA,QACJ,EAAE,QAAQ,KAAK,gBAAgB,OAAO;AAAA,MACxC;AAAA,IACF;AAEA,SAAK,UAAU;AACf,WAAO,MAAM,EAAE,SAAS,oCAAoC,CAAC;AAAA,EAC/D;AAAA;AAAA,EAGO,OAAa;AAClB,QAAI,CAAC,KAAK,QAAS;AAGnB,SAAK,gBAAgB,MAAM;AAE3B,QAAI;AACF,WAAK,IAAI,oBAAoB,QAAQ;AAAA,IACvC,SAAS,OAAO;AACd,aAAO,KAAK,EAAE,SAAS,kDAAkD,MAAM,CAAC;AAAA,IAClF;AACA,SAAK,IAAI,qBAAqB;AAG9B,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,SAAK,UAAU;AACf,WAAO,MAAM,EAAE,SAAS,oCAAoC,CAAC;AAAA,EAC/D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -25,7 +25,7 @@ class WebServerPerformanceWrapper {
|
|
|
25
25
|
// 1 second
|
|
26
26
|
includeHeaders: false,
|
|
27
27
|
includeUserAgent: true,
|
|
28
|
-
skipRoutes: ["/health", "/metrics"],
|
|
28
|
+
skipRoutes: ["/health/live", "/health/ready", "/metrics"],
|
|
29
29
|
skipMethods: []
|
|
30
30
|
};
|
|
31
31
|
const config = { ...defaultOptions, ...options };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/performance/webserver-performance.ts"],
|
|
4
|
-
"sourcesContent": ["import type { FastifyReply, FastifyRequest } from 'fastify';\nimport { PerformanceMonitor } from './performance-monitor.js';\n\nexport interface WebServerPerformanceOptions {\n logSlowRequests?: boolean;\n slowRequestThreshold?: number;\n includeHeaders?: boolean;\n includeUserAgent?: boolean;\n skipRoutes?: string[];\n skipMethods?: string[];\n}\n\nexport interface HttpRequestMetadata {\n method: string;\n url: string;\n ip?: string;\n userAgent?: string;\n headers?: Record<string, string | string[] | undefined>;\n statusCode?: number;\n contentLength?: number;\n error?: string;\n errorName?: string;\n argumentCount?: number;\n}\n\nexport class WebServerPerformanceWrapper {\n private static performanceMonitor: PerformanceMonitor;\n\n public static setPerformanceMonitor(monitor: PerformanceMonitor): void {\n WebServerPerformanceWrapper.performanceMonitor = monitor;\n }\n\n private static getPerformanceMonitor(): PerformanceMonitor {\n if (!WebServerPerformanceWrapper.performanceMonitor) {\n WebServerPerformanceWrapper.performanceMonitor = PerformanceMonitor.getInstance();\n }\n return WebServerPerformanceWrapper.performanceMonitor;\n }\n\n /**\n * Create performance middleware for Fastify\n */\n public static createPerformanceMiddleware(options: WebServerPerformanceOptions = {}) {\n const defaultOptions: WebServerPerformanceOptions = {\n logSlowRequests: true,\n slowRequestThreshold: 1000, // 1 second\n includeHeaders: false,\n includeUserAgent: true,\n skipRoutes: ['/health', '/metrics'],\n skipMethods: [],\n };\n\n const config = { ...defaultOptions, ...options };\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return async (request: FastifyRequest, _reply: FastifyReply) => {\n const { method, url, headers, ip } = request;\n const routeKey = `${method} ${url}`;\n\n // Skip monitoring for specified routes or methods\n if (config.skipRoutes?.includes(url) || config.skipMethods?.includes(method)) {\n return;\n }\n\n const startMark = monitor.startMeasure(routeKey, 'http');\n\n // Add request metadata\n const metadata: HttpRequestMetadata = {\n method,\n url,\n ip,\n userAgent: config.includeUserAgent ? (headers['user-agent'] as string) : undefined,\n headers: config.includeHeaders ? headers : undefined,\n };\n\n // Store metadata on request for later use\n (request as any).performanceMetadata = metadata;\n (request as any).performanceStartMark = startMark;\n };\n }\n\n /**\n * Create Fastify hooks for performance monitoring\n */\n public static createPerformanceHooks(_options: WebServerPerformanceOptions = {}) {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return {\n onSend: async (request: FastifyRequest, reply: FastifyReply, payload: any) => {\n const startMark = (request as any).performanceStartMark;\n const metadata = (request as any).performanceMetadata;\n\n if (startMark && metadata) {\n const responseMetadata: HttpRequestMetadata = {\n ...metadata,\n statusCode: reply.statusCode,\n contentLength: payload ? String(payload).length : 0,\n };\n\n monitor.endMeasure(startMark, responseMetadata);\n }\n\n return payload;\n },\n onError: async (request: FastifyRequest, reply: FastifyReply, error: Error) => {\n const startMark = (request as any).performanceStartMark;\n const metadata = (request as any).performanceMetadata;\n\n if (startMark && metadata) {\n const errorMetadata: HttpRequestMetadata = {\n ...metadata,\n statusCode: reply.statusCode,\n error: error.message,\n errorName: error.name,\n };\n\n monitor.endMeasure(startMark, errorMetadata);\n }\n },\n };\n }\n\n /**\n * Monitor controller method execution\n */\n public static async monitorControllerMethod<T>({\n controllerName,\n methodName,\n operation,\n metadata,\n }: {\n controllerName: string;\n methodName: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `${controllerName}.${methodName}`,\n type: 'http',\n fn: operation,\n metadata: {\n controller: controllerName,\n method: methodName,\n ...metadata,\n },\n });\n }\n\n /**\n * Monitor route handler execution\n */\n public static async monitorRouteHandler<T>({\n route,\n method,\n operation,\n metadata,\n }: {\n route: string;\n method: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `${method} ${route}`,\n type: 'http',\n fn: operation,\n metadata: { route, method, ...metadata },\n });\n }\n\n /**\n * Monitor middleware execution\n */\n public static async monitorMiddleware<T>({\n middlewareName,\n operation,\n metadata,\n }: {\n middlewareName: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `middleware.${middlewareName}`,\n type: 'http',\n fn: operation,\n metadata: {\n middleware: middlewareName,\n ...metadata,\n },\n });\n }\n}\n\n/**\n * Decorator for monitoring controller methods\n */\nexport function MonitorControllerMethod(methodName?: string) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n const controllerName = target.constructor.name;\n const operation = methodName ?? propertyKey;\n\n descriptor.value = async function (...args: any[]) {\n return WebServerPerformanceWrapper.monitorControllerMethod({\n controllerName,\n methodName: operation,\n operation: () => originalMethod.apply(this, args),\n metadata: { argumentCount: args.length },\n });\n };\n\n return descriptor;\n };\n}\n"],
|
|
5
|
-
"mappings": ";;AACA,SAAS,0BAA0B;AAwB5B,MAAM,4BAA4B;AAAA,EAzBzC,OAyByC;AAAA;AAAA;AAAA,EACvC,OAAe;AAAA,EAEf,OAAc,sBAAsB,SAAmC;AACrE,gCAA4B,qBAAqB;AAAA,EACnD;AAAA,EAEA,OAAe,wBAA4C;AACzD,QAAI,CAAC,4BAA4B,oBAAoB;AACnD,kCAA4B,qBAAqB,mBAAmB,YAAY;AAAA,IAClF;AACA,WAAO,4BAA4B;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAc,4BAA4B,UAAuC,CAAC,GAAG;AACnF,UAAM,iBAA8C;AAAA,MAClD,iBAAiB;AAAA,MACjB,sBAAsB;AAAA;AAAA,MACtB,gBAAgB;AAAA,MAChB,kBAAkB;AAAA,MAClB,YAAY,CAAC,
|
|
4
|
+
"sourcesContent": ["import type { FastifyReply, FastifyRequest } from 'fastify';\nimport { PerformanceMonitor } from './performance-monitor.js';\n\nexport interface WebServerPerformanceOptions {\n logSlowRequests?: boolean;\n slowRequestThreshold?: number;\n includeHeaders?: boolean;\n includeUserAgent?: boolean;\n skipRoutes?: string[];\n skipMethods?: string[];\n}\n\nexport interface HttpRequestMetadata {\n method: string;\n url: string;\n ip?: string;\n userAgent?: string;\n headers?: Record<string, string | string[] | undefined>;\n statusCode?: number;\n contentLength?: number;\n error?: string;\n errorName?: string;\n argumentCount?: number;\n}\n\nexport class WebServerPerformanceWrapper {\n private static performanceMonitor: PerformanceMonitor;\n\n public static setPerformanceMonitor(monitor: PerformanceMonitor): void {\n WebServerPerformanceWrapper.performanceMonitor = monitor;\n }\n\n private static getPerformanceMonitor(): PerformanceMonitor {\n if (!WebServerPerformanceWrapper.performanceMonitor) {\n WebServerPerformanceWrapper.performanceMonitor = PerformanceMonitor.getInstance();\n }\n return WebServerPerformanceWrapper.performanceMonitor;\n }\n\n /**\n * Create performance middleware for Fastify\n */\n public static createPerformanceMiddleware(options: WebServerPerformanceOptions = {}) {\n const defaultOptions: WebServerPerformanceOptions = {\n logSlowRequests: true,\n slowRequestThreshold: 1000, // 1 second\n includeHeaders: false,\n includeUserAgent: true,\n skipRoutes: ['/health/live', '/health/ready', '/metrics'],\n skipMethods: [],\n };\n\n const config = { ...defaultOptions, ...options };\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return async (request: FastifyRequest, _reply: FastifyReply) => {\n const { method, url, headers, ip } = request;\n const routeKey = `${method} ${url}`;\n\n // Skip monitoring for specified routes or methods\n if (config.skipRoutes?.includes(url) || config.skipMethods?.includes(method)) {\n return;\n }\n\n const startMark = monitor.startMeasure(routeKey, 'http');\n\n // Add request metadata\n const metadata: HttpRequestMetadata = {\n method,\n url,\n ip,\n userAgent: config.includeUserAgent ? (headers['user-agent'] as string) : undefined,\n headers: config.includeHeaders ? headers : undefined,\n };\n\n // Store metadata on request for later use\n (request as any).performanceMetadata = metadata;\n (request as any).performanceStartMark = startMark;\n };\n }\n\n /**\n * Create Fastify hooks for performance monitoring\n */\n public static createPerformanceHooks(_options: WebServerPerformanceOptions = {}) {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return {\n onSend: async (request: FastifyRequest, reply: FastifyReply, payload: any) => {\n const startMark = (request as any).performanceStartMark;\n const metadata = (request as any).performanceMetadata;\n\n if (startMark && metadata) {\n const responseMetadata: HttpRequestMetadata = {\n ...metadata,\n statusCode: reply.statusCode,\n contentLength: payload ? String(payload).length : 0,\n };\n\n monitor.endMeasure(startMark, responseMetadata);\n }\n\n return payload;\n },\n onError: async (request: FastifyRequest, reply: FastifyReply, error: Error) => {\n const startMark = (request as any).performanceStartMark;\n const metadata = (request as any).performanceMetadata;\n\n if (startMark && metadata) {\n const errorMetadata: HttpRequestMetadata = {\n ...metadata,\n statusCode: reply.statusCode,\n error: error.message,\n errorName: error.name,\n };\n\n monitor.endMeasure(startMark, errorMetadata);\n }\n },\n };\n }\n\n /**\n * Monitor controller method execution\n */\n public static async monitorControllerMethod<T>({\n controllerName,\n methodName,\n operation,\n metadata,\n }: {\n controllerName: string;\n methodName: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `${controllerName}.${methodName}`,\n type: 'http',\n fn: operation,\n metadata: {\n controller: controllerName,\n method: methodName,\n ...metadata,\n },\n });\n }\n\n /**\n * Monitor route handler execution\n */\n public static async monitorRouteHandler<T>({\n route,\n method,\n operation,\n metadata,\n }: {\n route: string;\n method: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `${method} ${route}`,\n type: 'http',\n fn: operation,\n metadata: { route, method, ...metadata },\n });\n }\n\n /**\n * Monitor middleware execution\n */\n public static async monitorMiddleware<T>({\n middlewareName,\n operation,\n metadata,\n }: {\n middlewareName: string;\n operation: () => Promise<T>;\n metadata?: Record<string, any>;\n }): Promise<T> {\n const monitor = WebServerPerformanceWrapper.getPerformanceMonitor();\n\n return monitor.measureAsync({\n name: `middleware.${middlewareName}`,\n type: 'http',\n fn: operation,\n metadata: {\n middleware: middlewareName,\n ...metadata,\n },\n });\n }\n}\n\n/**\n * Decorator for monitoring controller methods\n */\nexport function MonitorControllerMethod(methodName?: string) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n const controllerName = target.constructor.name;\n const operation = methodName ?? propertyKey;\n\n descriptor.value = async function (...args: any[]) {\n return WebServerPerformanceWrapper.monitorControllerMethod({\n controllerName,\n methodName: operation,\n operation: () => originalMethod.apply(this, args),\n metadata: { argumentCount: args.length },\n });\n };\n\n return descriptor;\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/queue/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAgE,MAAM,QAAQ,CAAC;AAEhG,OAAO,KAAK,EAAE,6BAA6B,EAAuB,MAAM,wBAAwB,CAAC;AAOjG,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/queue/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAgE,MAAM,QAAQ,CAAC;AAEhG,OAAO,KAAK,EAAE,6BAA6B,EAAuB,MAAM,wBAAwB,CAAC;AAOjG,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItD,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,MAAM,CAAyB;IAEvC,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,OAAO,CAAsB;IAErC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,YAAY,CAAC,CAAe;IAEpC,OAAO,CAAC,MAAM,CAAiC;IAE/C,OAAO,CAAC,aAAa,CAAyC;gBAElD,EACV,iBAAiB,EACjB,OAAO,EACP,MAAM,EAAE,OAAO,EACf,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,EAAE,6BAA6B;IAcnB,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC/E,OAAO,CAAC,aAAa;IAyDrB,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,YAAY,CAElB;IAEF,OAAO,CAAC,cAAc,CAIpB;IAEF,OAAO,CAAC,eAAe,CAOrB;IAEF,OAAO,CAAC,cAAc,CAEpB;IAEK,aAAa,GAAU,0BAA0B;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAE,gDA0B5G;IAEF,OAAO,CAAC,eAAe,CAuCrB;IAEW,qBAAqB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IA2BpD;;OAEG;IACI,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGlE"}
|
package/dist/queue/manager.js
CHANGED
|
@@ -4,8 +4,7 @@ import { Queue } from "bullmq";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { Logger } from "../logger/index.js";
|
|
6
6
|
import QueueWorker from "./worker.js";
|
|
7
|
-
import { Helper, Loader } from "../util/index.js";
|
|
8
|
-
import { existsSync } from "fs";
|
|
7
|
+
import { File, Helper, Loader, Time } from "../util/index.js";
|
|
9
8
|
class QueueManager {
|
|
10
9
|
static {
|
|
11
10
|
__name(this, "QueueManager");
|
|
@@ -37,14 +36,8 @@ class QueueManager {
|
|
|
37
36
|
if (!queues) {
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
|
-
const processorsDirectoryExists = await
|
|
39
|
+
const processorsDirectoryExists = await File.pathExists(this.options.processorsDirectory);
|
|
41
40
|
if (!processorsDirectoryExists) {
|
|
42
|
-
Logger.warn({
|
|
43
|
-
message: "Processors directory not found",
|
|
44
|
-
meta: {
|
|
45
|
-
Directory: this.options.processorsDirectory
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
41
|
return;
|
|
49
42
|
}
|
|
50
43
|
try {
|
|
@@ -183,7 +176,7 @@ class QueueManager {
|
|
|
183
176
|
if (!job) {
|
|
184
177
|
return;
|
|
185
178
|
}
|
|
186
|
-
const startTime =
|
|
179
|
+
const startTime = Time.now();
|
|
187
180
|
job.updateData({ ...job.data, startTime });
|
|
188
181
|
this.log("Worker processing...", {
|
|
189
182
|
Queue: job.queueName,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/queue/manager.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Job, type Processor, Queue, type QueueOptions, type WorkerOptions } from 'bullmq';\nimport path from 'path';\nimport type { QueueManagerConstructorParams, QueueManagerOptions } from './manager.interface.js';\nimport type { RedisInstance } from '../redis/index.js';\nimport type { DatabaseInstance } from '../database/index.js';\nimport { Logger } from '../logger/index.js';\nimport QueueWorker from './worker.js';\nimport type BaseProcessor from './processor/base.js';\nimport { Helper, Loader } from '../util/index.js';\nimport type { QueueJob, QueueJobData } from './job.interface.js';\nimport type { ProcessorConstructor } from './processor/processor.interface.js';\nimport type { QueueItem } from './index.interface.js';\nimport
|
|
5
|
-
"mappings": ";;AAAA,SAAmC,aAAoD;AACvF,OAAO,UAAU;AAIjB,SAAS,cAAc;AACvB,OAAO,iBAAiB;AAExB,SAAS,QAAQ,
|
|
4
|
+
"sourcesContent": ["import { type Job, type Processor, Queue, type QueueOptions, type WorkerOptions } from 'bullmq';\nimport path from 'path';\nimport type { QueueManagerConstructorParams, QueueManagerOptions } from './manager.interface.js';\nimport type { RedisInstance } from '../redis/index.js';\nimport type { DatabaseInstance } from '../database/index.js';\nimport { Logger } from '../logger/index.js';\nimport QueueWorker from './worker.js';\nimport type BaseProcessor from './processor/base.js';\nimport { File, Helper, Loader, Time } from '../util/index.js';\nimport type { QueueJob, QueueJobData } from './job.interface.js';\nimport type { ProcessorConstructor } from './processor/processor.interface.js';\nimport type { QueueItem } from './index.interface.js';\nimport type { ApplicationConfig } from '../application/base-application.interface.js';\nimport type EventManager from '../event/manager.js';\n\nexport default class QueueManager {\n private logger: typeof Logger = Logger;\n\n private applicationConfig: ApplicationConfig;\n\n private options: QueueManagerOptions;\n\n private redisInstance: RedisInstance;\n private databaseInstance: DatabaseInstance | null;\n private eventManager?: EventManager;\n\n private queues: Map<string, Queue> = new Map();\n\n private jobProcessors: Map<string, BaseProcessor> = new Map();\n\n constructor({\n applicationConfig,\n options,\n queues: _queues,\n redisInstance,\n databaseInstance,\n eventManager,\n }: QueueManagerConstructorParams) {\n // Define default options\n const defaultOptions: Partial<QueueManagerOptions> = {};\n\n // Merge options\n this.options = Helper.defaultsDeep(options, defaultOptions);\n\n this.applicationConfig = applicationConfig;\n\n this.redisInstance = redisInstance;\n this.databaseInstance = databaseInstance;\n this.eventManager = eventManager;\n }\n\n public async registerQueues({ queues }: { queues: QueueItem[] }): Promise<void> {\n if (!queues) {\n return;\n }\n\n // Check if processors directory exists\n const processorsDirectoryExists = await File.pathExists(this.options.processorsDirectory);\n\n if (!processorsDirectoryExists) {\n return;\n }\n\n try {\n const jobProcessorClasses = await Loader.loadModulesInDirectory({\n directory: this.options.processorsDirectory,\n extensions: ['.ts', '.js'],\n });\n\n for (const queue of queues) {\n this.registerQueue({ queue, jobProcessorClasses });\n }\n\n if (this.applicationConfig.queue.log?.queuesRegistered) {\n this.log('Registered queue', {\n 'Queue Count': queues.length,\n 'Job Count': this.jobProcessors.size,\n });\n }\n } catch (error) {\n Logger.error({ error });\n }\n }\n\n private registerQueue({ queue, jobProcessorClasses }: { queue: QueueItem; jobProcessorClasses: any }): void {\n if (!queue.jobs) {\n Logger.warn({\n message: 'No jobs found for queue, skip register',\n meta: {\n Name: queue.name,\n },\n });\n\n return;\n }\n\n const queueOptions: QueueOptions = {\n connection: this.redisInstance.client,\n defaultJobOptions: {\n removeOnComplete: true,\n removeOnFail: true,\n },\n };\n\n const queueInstance = new Queue(queue.name, queueOptions);\n\n queueInstance.on('error', this.onQueueError);\n queueInstance.on('waiting', this.onQueueWaiting);\n queueInstance.on('progress', this.onQueueProgress);\n queueInstance.on('removed', this.onQueueRemoved);\n\n if (!queue.isExternal) {\n const workerOptions: WorkerOptions = {\n connection: this.redisInstance.client,\n autorun: true,\n };\n\n new QueueWorker({\n applicationConfig: this.applicationConfig,\n queueManager: this,\n name: queue.name,\n processor: this.workerProcessor,\n options: workerOptions,\n redisInstance: this.redisInstance,\n });\n }\n\n this.queues.set(queue.name, queueInstance);\n\n if (this.applicationConfig.queue.log?.queueRegistered) {\n this.log('Registered queue', { Name: queue.name });\n }\n\n // Register job processors\n this.registerJobProcessors({\n queue,\n jobs: queue.jobs,\n jobProcessorClasses,\n });\n }\n\n private registerJobProcessors({\n queue,\n jobs,\n jobProcessorClasses,\n }: {\n queue: QueueItem;\n jobs: QueueJob[];\n jobProcessorClasses: Record<string, ProcessorConstructor>;\n }): void {\n if (!jobs) {\n return;\n }\n\n const scriptFileExtension = Helper.getScriptFileExtension();\n\n for (const job of jobs) {\n if (!queue.isExternal) {\n const ProcessorClass = jobProcessorClasses[job.id];\n\n if (!ProcessorClass) {\n const jobPath = path.join(this.options.processorsDirectory, `${job.id}.${scriptFileExtension}`);\n\n throw new Error(`Processor class not found (Job ID: ${job.id} | Path: ${jobPath})`);\n }\n\n const processorInstance = new ProcessorClass(\n this,\n this.applicationConfig,\n this.redisInstance,\n this.databaseInstance,\n this.eventManager,\n );\n\n this.jobProcessors.set(job.id, processorInstance);\n }\n\n if (this.applicationConfig.queue.log?.jobRegistered) {\n this.log('Job registered', { ID: job.id });\n }\n }\n }\n\n private onQueueError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private onQueueWaiting = (job: Job): void => {\n if (this.applicationConfig.queue.log?.queueWaiting) {\n this.log('Waiting...', { Queue: job.queueName, Job: job.id });\n }\n };\n\n private onQueueProgress = (job: Job<any, any, string>, progress: number | object): void => {\n this.log('Progress update', {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n Progress: progress,\n });\n };\n\n private onQueueRemoved = (job: Job): void => {\n this.log('Removed queue', { Queue: job.queueName, Job: job.id });\n };\n\n public addJobToQueue = async ({ queueId, jobId, data }: { queueId: string; jobId: string; data: QueueJobData }) => {\n const queue = this.queues.get(queueId);\n\n if (!queue) {\n this.log('Queue not found', { 'Queue ID': queueId });\n\n return;\n }\n\n const job = await queue.add(jobId, data);\n\n const dataStr = JSON.stringify(data);\n\n const maxLogDataStrLength = 50;\n const truncatedLogDataStr =\n dataStr.length > maxLogDataStrLength ? `${dataStr.substring(0, maxLogDataStrLength)}...` : dataStr;\n\n if (this.applicationConfig.queue.log?.jobAdded) {\n this.log('Job added', {\n Queue: queueId,\n 'Job ID': jobId,\n Data: truncatedLogDataStr,\n });\n }\n\n return job;\n };\n\n private workerProcessor = async (job: Job): Promise<Processor<any, any, string> | undefined> => {\n if (!job) {\n return;\n }\n\n const startTime = Time.now();\n\n // Add start time to job data\n job.updateData({ ...job.data, startTime });\n\n this.log('Worker processing...', {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n });\n\n const processor = this.jobProcessors.get(job.name);\n\n if (!processor) {\n throw new Error(`No processor registered for job (Name: ${job.name})`);\n }\n\n try {\n const jobResult = await processor.process({ job });\n\n return jobResult;\n } catch (error) {\n Logger.warn({\n message: 'Queue worker processing error',\n meta: {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n Error: (error as Error).message,\n },\n });\n\n Logger.error({ error });\n }\n };\n\n public async listAllJobsWithStatus(): Promise<any[]> {\n const jobsSummary: any[] = [];\n\n for (const [queueName, queue] of this.queues) {\n const jobStates = ['active', 'waiting', 'completed', 'failed', 'delayed', 'paused'];\n\n const jobsDetailsPromises = jobStates.map(async (state: any) => {\n const jobs = await queue.getJobs([state]);\n return jobs.map(job => ({\n id: job.id,\n name: job.name,\n queueName,\n state,\n attemptsMade: job.attemptsMade,\n failedReason: job.failedReason,\n }));\n });\n\n const results = await Promise.all(jobsDetailsPromises);\n const flattenedResults = results.flat();\n\n jobsSummary.push(...flattenedResults);\n }\n\n return jobsSummary;\n }\n\n /**\n * Log queue message\n */\n public log(message: string, meta?: Record<string, unknown>): void {\n this.logger.custom({ level: 'queue', message, meta });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/queue/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/queue/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAQ1E,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,MAAM;IAC7C,OAAO,CAAC,iBAAiB,CAAoB;IAE7C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,EACV,iBAAiB,EACjB,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,OAAO,EACP,aAAa,GACd,EAAE,4BAA4B;IAe/B,OAAO,CAAC,cAAc,CAMpB;IAEF,OAAO,CAAC,aAAa,CAEnB;IAEF,OAAO,CAAC,cAAc,CAcpB;IAEF,OAAO,CAAC,eAAe,CAErB;IAEF,OAAO,CAAC,iBAAiB,CAwBvB;CACH"}
|
package/dist/queue/worker.js
CHANGED
|
@@ -2,6 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
import { Worker } from "bullmq";
|
|
4
4
|
import { Logger } from "../logger/index.js";
|
|
5
|
+
import { Time } from "../util/index.js";
|
|
5
6
|
import { WebSocketRedisSubscriberEvent } from "../websocket/websocket.interface.js";
|
|
6
7
|
class QueueWorker extends Worker {
|
|
7
8
|
static {
|
|
@@ -53,8 +54,7 @@ class QueueWorker extends Worker {
|
|
|
53
54
|
);
|
|
54
55
|
}
|
|
55
56
|
const startTime = jobData.startTime;
|
|
56
|
-
const
|
|
57
|
-
const executionTimeMs = seconds * 1e3 + nanoseconds / 1e6;
|
|
57
|
+
const executionTimeMs = Time.calculateElapsedTimeMs({ startTime });
|
|
58
58
|
const formattedExecutionTime = executionTimeMs.toFixed(2);
|
|
59
59
|
if (this.applicationConfig.queue.log?.jobCompleted) {
|
|
60
60
|
this.queueManager.log("Job completed", {
|
package/dist/queue/worker.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/queue/worker.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Job, type Processor, Worker } from 'bullmq';\nimport type { QueueWorkerConstructorParams } from './worker.interface.js';\nimport type { RedisInstance } from '../redis/index.js';\nimport { Logger } from '../logger/index.js';\nimport { WebSocketRedisSubscriberEvent } from '../websocket/websocket.interface.js';\nimport type { ApplicationConfig } from '../application/base-application.interface.js';\nimport type QueueManager from './manager.js';\n\nexport default class QueueWorker extends Worker {\n private applicationConfig: ApplicationConfig;\n\n private queueManager: QueueManager;\n private redisInstance: RedisInstance;\n\n constructor({\n applicationConfig,\n queueManager,\n name,\n processor,\n options,\n redisInstance,\n }: QueueWorkerConstructorParams) {\n super(name, processor, options);\n\n this.applicationConfig = applicationConfig;\n\n this.queueManager = queueManager;\n this.redisInstance = redisInstance;\n\n this.on('active', this.onWorkerActive);\n this.on('error', this.onWorkerError);\n this.on('failed', this.onWorkerFailed);\n this.on('stalled', this.onWorkerStalled);\n this.on('completed', this.onWorkerCompleted);\n }\n\n private onWorkerActive = (job: Job): void => {\n this.queueManager.log('Worker active', {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n });\n };\n\n private onWorkerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private onWorkerFailed = (job: Job<any, Processor<any, any, string>, string> | undefined, error: Error): void => {\n // // Send job failed message to client\n // if (job && job.data.webSocketClientId) {\n // const errorMessage = {\n // webSocketClientId: job.data.webSocketClientId,\n // action: job.name,\n // error: error.message,\n // };\n\n // // Send error message to client\n // this.redisInstance.publisherClient.publish('queueJobError', JSON.stringify(errorMessage));\n // }\n\n Logger.error({ error });\n };\n\n private onWorkerStalled = (jobId: string): void => {\n this.queueManager.log('Worker stalled', { Job: jobId });\n };\n\n private onWorkerCompleted = (job: Job): void => {\n const jobData = job.data;\n\n if (job.returnvalue?.webSocketClientId) {\n // Send job completed message to client\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n JSON.stringify(job.returnvalue),\n );\n }\n\n const startTime = jobData.startTime;\n\n const
|
|
5
|
-
"mappings": ";;AAAA,SAAmC,cAAc;AAGjD,SAAS,cAAc;AACvB,SAAS,qCAAqC;AAI9C,MAAO,oBAAkC,OAAO;AAAA,
|
|
4
|
+
"sourcesContent": ["import { type Job, type Processor, Worker } from 'bullmq';\nimport type { QueueWorkerConstructorParams } from './worker.interface.js';\nimport type { RedisInstance } from '../redis/index.js';\nimport { Logger } from '../logger/index.js';\nimport { Time } from '../util/index.js';\nimport { WebSocketRedisSubscriberEvent } from '../websocket/websocket.interface.js';\nimport type { ApplicationConfig } from '../application/base-application.interface.js';\nimport type QueueManager from './manager.js';\n\nexport default class QueueWorker extends Worker {\n private applicationConfig: ApplicationConfig;\n\n private queueManager: QueueManager;\n private redisInstance: RedisInstance;\n\n constructor({\n applicationConfig,\n queueManager,\n name,\n processor,\n options,\n redisInstance,\n }: QueueWorkerConstructorParams) {\n super(name, processor, options);\n\n this.applicationConfig = applicationConfig;\n\n this.queueManager = queueManager;\n this.redisInstance = redisInstance;\n\n this.on('active', this.onWorkerActive);\n this.on('error', this.onWorkerError);\n this.on('failed', this.onWorkerFailed);\n this.on('stalled', this.onWorkerStalled);\n this.on('completed', this.onWorkerCompleted);\n }\n\n private onWorkerActive = (job: Job): void => {\n this.queueManager.log('Worker active', {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n });\n };\n\n private onWorkerError = (error: Error): void => {\n Logger.error({ error });\n };\n\n private onWorkerFailed = (job: Job<any, Processor<any, any, string>, string> | undefined, error: Error): void => {\n // // Send job failed message to client\n // if (job && job.data.webSocketClientId) {\n // const errorMessage = {\n // webSocketClientId: job.data.webSocketClientId,\n // action: job.name,\n // error: error.message,\n // };\n\n // // Send error message to client\n // this.redisInstance.publisherClient.publish('queueJobError', JSON.stringify(errorMessage));\n // }\n\n Logger.error({ error });\n };\n\n private onWorkerStalled = (jobId: string): void => {\n this.queueManager.log('Worker stalled', { Job: jobId });\n };\n\n private onWorkerCompleted = (job: Job): void => {\n const jobData = job.data;\n\n if (job.returnvalue?.webSocketClientId) {\n // Send job completed message to client\n this.redisInstance.publisherClient.publish(\n WebSocketRedisSubscriberEvent.QueueJobCompleted,\n JSON.stringify(job.returnvalue),\n );\n }\n\n const startTime = jobData.startTime;\n\n const executionTimeMs = Time.calculateElapsedTimeMs({ startTime });\n const formattedExecutionTime = executionTimeMs.toFixed(2);\n\n if (this.applicationConfig.queue.log?.jobCompleted) {\n this.queueManager.log('Job completed', {\n Queue: job.queueName,\n 'Job Name': job.name,\n 'Job ID': job.id,\n Time: `${formattedExecutionTime}ms`,\n });\n }\n };\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,SAAmC,cAAc;AAGjD,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,qCAAqC;AAI9C,MAAO,oBAAkC,OAAO;AAAA,EAThD,OASgD;AAAA;AAAA;AAAA,EACtC;AAAA,EAEA;AAAA,EACA;AAAA,EAER,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAiC;AAC/B,UAAM,MAAM,WAAW,OAAO;AAE9B,SAAK,oBAAoB;AAEzB,SAAK,eAAe;AACpB,SAAK,gBAAgB;AAErB,SAAK,GAAG,UAAU,KAAK,cAAc;AACrC,SAAK,GAAG,SAAS,KAAK,aAAa;AACnC,SAAK,GAAG,UAAU,KAAK,cAAc;AACrC,SAAK,GAAG,WAAW,KAAK,eAAe;AACvC,SAAK,GAAG,aAAa,KAAK,iBAAiB;AAAA,EAC7C;AAAA,EAEQ,iBAAiB,wBAAC,QAAmB;AAC3C,SAAK,aAAa,IAAI,iBAAiB;AAAA,MACrC,OAAO,IAAI;AAAA,MACX,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,IAChB,CAAC;AAAA,EACH,GANyB;AAAA,EAQjB,gBAAgB,wBAAC,UAAuB;AAC9C,WAAO,MAAM,EAAE,MAAM,CAAC;AAAA,EACxB,GAFwB;AAAA,EAIhB,iBAAiB,wBAAC,KAAgE,UAAuB;AAa/G,WAAO,MAAM,EAAE,MAAM,CAAC;AAAA,EACxB,GAdyB;AAAA,EAgBjB,kBAAkB,wBAAC,UAAwB;AACjD,SAAK,aAAa,IAAI,kBAAkB,EAAE,KAAK,MAAM,CAAC;AAAA,EACxD,GAF0B;AAAA,EAIlB,oBAAoB,wBAAC,QAAmB;AAC9C,UAAM,UAAU,IAAI;AAEpB,QAAI,IAAI,aAAa,mBAAmB;AAEtC,WAAK,cAAc,gBAAgB;AAAA,QACjC,8BAA8B;AAAA,QAC9B,KAAK,UAAU,IAAI,WAAW;AAAA,MAChC;AAAA,IACF;AAEA,UAAM,YAAY,QAAQ;AAE1B,UAAM,kBAAkB,KAAK,uBAAuB,EAAE,UAAU,CAAC;AACjE,UAAM,yBAAyB,gBAAgB,QAAQ,CAAC;AAExD,QAAI,KAAK,kBAAkB,MAAM,KAAK,cAAc;AAClD,WAAK,aAAa,IAAI,iBAAiB;AAAA,QACrC,OAAO,IAAI;AAAA,QACX,YAAY,IAAI;AAAA,QAChB,UAAU,IAAI;AAAA,QACd,MAAM,GAAG,sBAAsB;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF,GAxB4B;AAyB9B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/redis/manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/redis/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,aAAa,MAAM,eAAe,CAAC;AA2I1C,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,MAAM,CAAyB;IAEvC,OAAO,CAAC,OAAO,CAAsB;IAE9B,SAAS,EAAE,aAAa,EAAE,CAAM;gBAE3B,MAAM,EAAE,mBAAmB;IAI1B,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC;IA8GjC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDxC;;OAEG;IACI,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGlE"}
|
package/dist/redis/manager.js
CHANGED
|
@@ -1,8 +1,111 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
import { Redis } from "ioredis";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
4
5
|
import RedisInstance from "./instance.js";
|
|
5
6
|
import { Logger } from "../logger/index.js";
|
|
7
|
+
import { CachePerformanceWrapper } from "../performance/index.js";
|
|
8
|
+
const truthyPattern = /^(1|true|yes|on)$/i;
|
|
9
|
+
const scheduleMicrotask = typeof globalThis.queueMicrotask === "function" ? globalThis.queueMicrotask.bind(globalThis) : (callback) => {
|
|
10
|
+
void Promise.resolve().then(callback);
|
|
11
|
+
};
|
|
12
|
+
class InMemoryRedisClient extends EventEmitter {
|
|
13
|
+
static {
|
|
14
|
+
__name(this, "InMemoryRedisClient");
|
|
15
|
+
}
|
|
16
|
+
shared;
|
|
17
|
+
constructor(shared) {
|
|
18
|
+
super();
|
|
19
|
+
this.shared = shared;
|
|
20
|
+
scheduleMicrotask(() => {
|
|
21
|
+
this.emit("ready");
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
cleanupSubscriptions() {
|
|
25
|
+
for (const subscribers of this.shared.subscriptions.values()) {
|
|
26
|
+
subscribers.delete(this);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
clearExpirationForKey(key) {
|
|
30
|
+
const timer = this.shared.expirations.get(key);
|
|
31
|
+
if (timer) {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
this.shared.expirations.delete(key);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
ping(callback) {
|
|
37
|
+
if (callback) {
|
|
38
|
+
callback(null, "PONG");
|
|
39
|
+
return Promise.resolve("PONG");
|
|
40
|
+
}
|
|
41
|
+
return Promise.resolve("PONG");
|
|
42
|
+
}
|
|
43
|
+
async set(...args) {
|
|
44
|
+
const [key, value, mode, expiration] = args;
|
|
45
|
+
const serializedValue = value instanceof Buffer ? value : String(value);
|
|
46
|
+
this.shared.store.set(key, serializedValue);
|
|
47
|
+
this.clearExpirationForKey(key);
|
|
48
|
+
if (typeof mode === "string" && mode.toUpperCase() === "EX" && typeof expiration === "number") {
|
|
49
|
+
const timer = setTimeout(() => {
|
|
50
|
+
this.shared.store.delete(key);
|
|
51
|
+
this.shared.expirations.delete(key);
|
|
52
|
+
}, expiration * 1e3);
|
|
53
|
+
if (typeof timer.unref === "function") {
|
|
54
|
+
timer.unref();
|
|
55
|
+
}
|
|
56
|
+
this.shared.expirations.set(key, timer);
|
|
57
|
+
}
|
|
58
|
+
return "OK";
|
|
59
|
+
}
|
|
60
|
+
async get(key) {
|
|
61
|
+
return this.shared.store.get(key) ?? null;
|
|
62
|
+
}
|
|
63
|
+
async del(key) {
|
|
64
|
+
const existed = this.shared.store.delete(key);
|
|
65
|
+
this.clearExpirationForKey(key);
|
|
66
|
+
return existed ? 1 : 0;
|
|
67
|
+
}
|
|
68
|
+
async publish(channel, message) {
|
|
69
|
+
const subscribers = this.shared.subscriptions.get(channel);
|
|
70
|
+
if (!subscribers || subscribers.size === 0) {
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
for (const subscriber of subscribers) {
|
|
74
|
+
scheduleMicrotask(() => {
|
|
75
|
+
subscriber.emit("message", channel, message);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return subscribers.size;
|
|
79
|
+
}
|
|
80
|
+
async subscribe(channel) {
|
|
81
|
+
let subscribers = this.shared.subscriptions.get(channel);
|
|
82
|
+
if (!subscribers) {
|
|
83
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
84
|
+
this.shared.subscriptions.set(channel, subscribers);
|
|
85
|
+
}
|
|
86
|
+
subscribers.add(this);
|
|
87
|
+
return subscribers.size;
|
|
88
|
+
}
|
|
89
|
+
async unsubscribe(channel) {
|
|
90
|
+
const subscribers = this.shared.subscriptions.get(channel);
|
|
91
|
+
if (!subscribers) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
subscribers.delete(this);
|
|
95
|
+
return subscribers.size;
|
|
96
|
+
}
|
|
97
|
+
async quit() {
|
|
98
|
+
this.cleanupSubscriptions();
|
|
99
|
+
this.removeAllListeners();
|
|
100
|
+
this.emit("end");
|
|
101
|
+
return "OK";
|
|
102
|
+
}
|
|
103
|
+
disconnect() {
|
|
104
|
+
this.cleanupSubscriptions();
|
|
105
|
+
this.removeAllListeners();
|
|
106
|
+
this.emit("end");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
6
109
|
class RedisManager {
|
|
7
110
|
static {
|
|
8
111
|
__name(this, "RedisManager");
|
|
@@ -13,44 +116,136 @@ class RedisManager {
|
|
|
13
116
|
constructor(config) {
|
|
14
117
|
this.options = config;
|
|
15
118
|
}
|
|
16
|
-
connect() {
|
|
17
|
-
return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
119
|
+
async connect() {
|
|
120
|
+
return CachePerformanceWrapper.monitorConnection(
|
|
121
|
+
"connect",
|
|
122
|
+
async () => {
|
|
123
|
+
const startTime = performance.now();
|
|
124
|
+
const redisOptions = {
|
|
125
|
+
host: this.options.host,
|
|
126
|
+
port: this.options.port,
|
|
127
|
+
password: this.options.password,
|
|
128
|
+
maxRetriesPerRequest: null
|
|
129
|
+
// Needed for bullmq
|
|
130
|
+
};
|
|
131
|
+
const useInMemoryRedis = truthyPattern.test(process.env.PXL_REDIS_IN_MEMORY ?? "") || truthyPattern.test(process.env.REDIS_IN_MEMORY ?? "");
|
|
132
|
+
let sharedState;
|
|
133
|
+
if (useInMemoryRedis) {
|
|
134
|
+
sharedState = {
|
|
135
|
+
store: /* @__PURE__ */ new Map(),
|
|
136
|
+
expirations: /* @__PURE__ */ new Map(),
|
|
137
|
+
subscriptions: /* @__PURE__ */ new Map()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const createClient = /* @__PURE__ */ __name(() => {
|
|
141
|
+
if (useInMemoryRedis) {
|
|
142
|
+
if (!sharedState) {
|
|
143
|
+
throw new Error("In-memory Redis shared state not initialized");
|
|
144
|
+
}
|
|
145
|
+
return new InMemoryRedisClient(sharedState);
|
|
146
|
+
}
|
|
147
|
+
return new Redis(redisOptions);
|
|
148
|
+
}, "createClient");
|
|
149
|
+
const client = createClient();
|
|
150
|
+
const publisherClient = createClient();
|
|
151
|
+
const subscriberClient = createClient();
|
|
152
|
+
try {
|
|
153
|
+
await Promise.all([
|
|
154
|
+
new Promise((resolve, reject) => {
|
|
155
|
+
client.once("ready", () => resolve());
|
|
156
|
+
client.once("error", (error) => reject(error));
|
|
157
|
+
}),
|
|
158
|
+
new Promise((resolve, reject) => {
|
|
159
|
+
publisherClient.once("ready", () => resolve());
|
|
160
|
+
publisherClient.once("error", (error) => reject(error));
|
|
161
|
+
}),
|
|
162
|
+
new Promise((resolve, reject) => {
|
|
163
|
+
subscriberClient.once("ready", () => resolve());
|
|
164
|
+
subscriberClient.once("error", (error) => reject(error));
|
|
165
|
+
})
|
|
166
|
+
]);
|
|
167
|
+
const redisInstance = new RedisInstance({
|
|
168
|
+
redisManager: this,
|
|
169
|
+
client,
|
|
170
|
+
publisherClient,
|
|
171
|
+
subscriberClient
|
|
172
|
+
});
|
|
173
|
+
this.instances.push(redisInstance);
|
|
174
|
+
const duration = performance.now() - startTime;
|
|
175
|
+
const meta = {
|
|
38
176
|
Host: this.options.host,
|
|
39
|
-
Port: this.options.port
|
|
177
|
+
Port: this.options.port,
|
|
178
|
+
Duration: `${duration.toFixed(2)}ms`,
|
|
179
|
+
Mode: useInMemoryRedis ? "in-memory" : "network"
|
|
180
|
+
};
|
|
181
|
+
if (this.options.applicationConfig.log?.startUp) {
|
|
182
|
+
this.log("Connected", meta);
|
|
183
|
+
} else {
|
|
184
|
+
this.logger.debug({ message: "Redis connected", meta });
|
|
185
|
+
}
|
|
186
|
+
if (useInMemoryRedis) {
|
|
187
|
+
this.logger.debug({ message: "Using in-memory Redis stub" });
|
|
188
|
+
}
|
|
189
|
+
return redisInstance;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
const duration = performance.now() - startTime;
|
|
192
|
+
await Promise.allSettled([client.quit(), publisherClient.quit(), subscriberClient.quit()]);
|
|
193
|
+
this.logger.error({
|
|
194
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
195
|
+
message: "Redis connection failed",
|
|
196
|
+
meta: {
|
|
197
|
+
Host: this.options.host,
|
|
198
|
+
Port: this.options.port,
|
|
199
|
+
Duration: `${duration.toFixed(2)}ms`,
|
|
200
|
+
Mode: useInMemoryRedis ? "in-memory" : "network"
|
|
201
|
+
}
|
|
40
202
|
});
|
|
203
|
+
throw error;
|
|
41
204
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Logger.error({ error });
|
|
46
|
-
reject(error);
|
|
47
|
-
}, "handleError");
|
|
48
|
-
client.on("connect", handleConnect);
|
|
49
|
-
client.on("error", handleError);
|
|
50
|
-
});
|
|
205
|
+
},
|
|
206
|
+
{ host: this.options.host, port: this.options.port }
|
|
207
|
+
);
|
|
51
208
|
}
|
|
52
209
|
async disconnect() {
|
|
53
|
-
await
|
|
210
|
+
await CachePerformanceWrapper.monitorConnection(
|
|
211
|
+
"disconnect",
|
|
212
|
+
async () => {
|
|
213
|
+
const startTime = performance.now();
|
|
214
|
+
const instanceCount = this.instances.length;
|
|
215
|
+
try {
|
|
216
|
+
await Promise.all(this.instances.map((instance) => instance.disconnect()));
|
|
217
|
+
const duration = performance.now() - startTime;
|
|
218
|
+
if (instanceCount > 0) {
|
|
219
|
+
const meta = {
|
|
220
|
+
Instances: instanceCount,
|
|
221
|
+
Host: this.options.host,
|
|
222
|
+
Port: this.options.port,
|
|
223
|
+
Duration: `${duration.toFixed(2)}ms`
|
|
224
|
+
};
|
|
225
|
+
if (this.options.applicationConfig.log?.startUp) {
|
|
226
|
+
this.log("Disconnected all Redis instances", meta);
|
|
227
|
+
} else {
|
|
228
|
+
this.logger.debug({ message: "Redis instances disconnected", meta });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
this.instances = [];
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const duration = performance.now() - startTime;
|
|
234
|
+
this.logger.error({
|
|
235
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
236
|
+
message: "Redis disconnection failed",
|
|
237
|
+
meta: {
|
|
238
|
+
Host: this.options.host,
|
|
239
|
+
Port: this.options.port,
|
|
240
|
+
Instances: instanceCount,
|
|
241
|
+
Duration: `${duration.toFixed(2)}ms`
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
{ host: this.options.host, port: this.options.port }
|
|
248
|
+
);
|
|
54
249
|
}
|
|
55
250
|
/**
|
|
56
251
|
* Log Redis message
|