@vtex/api 7.4.1-beta.1 → 7.4.2-beta.1
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/lib/service/master.d.ts +0 -2
- package/lib/service/master.js +2 -15
- package/lib/service/worker/index.d.ts +0 -1
- package/lib/service/worker/index.js +4 -17
- package/lib/service/worker/runtime/builtIn/middlewares.d.ts +1 -1
- package/lib/service/worker/runtime/builtIn/middlewares.js +2 -7
- package/lib/service/worker/runtime/statusTrack.js +4 -15
- package/package.json +3 -2
- package/lib/service/metrics/clusterMetricsAggregator.d.ts +0 -72
- package/lib/service/metrics/clusterMetricsAggregator.js +0 -164
package/lib/service/master.d.ts
CHANGED
package/lib/service/master.js
CHANGED
|
@@ -3,12 +3,11 @@ 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.startMaster =
|
|
6
|
+
exports.startMaster = void 0;
|
|
7
7
|
const cluster_1 = __importDefault(require("cluster"));
|
|
8
8
|
const os_1 = require("os");
|
|
9
9
|
const constants_1 = require("../constants");
|
|
10
10
|
const logger_1 = require("./logger");
|
|
11
|
-
const clusterMetricsAggregator_1 = require("./metrics/clusterMetricsAggregator");
|
|
12
11
|
const listeners_1 = require("./worker/listeners");
|
|
13
12
|
const statusTrack_1 = require("./worker/runtime/statusTrack");
|
|
14
13
|
let handledSignal;
|
|
@@ -20,12 +19,6 @@ const onMessage = (worker, message) => {
|
|
|
20
19
|
(0, statusTrack_1.trackStatus)();
|
|
21
20
|
(0, statusTrack_1.broadcastStatusTrack)();
|
|
22
21
|
}
|
|
23
|
-
else if ((0, clusterMetricsAggregator_1.isAggMetricsRequest)(message)) {
|
|
24
|
-
(0, clusterMetricsAggregator_1.handleWorkerMetricsRequest)(worker, message);
|
|
25
|
-
}
|
|
26
|
-
else if ((0, clusterMetricsAggregator_1.isPromClientMessage)(message)) {
|
|
27
|
-
// Handled by prom-client's own cluster listener; ignore here.
|
|
28
|
-
}
|
|
29
22
|
else {
|
|
30
23
|
listeners_1.logger.warn({
|
|
31
24
|
content: message,
|
|
@@ -34,7 +27,6 @@ const onMessage = (worker, message) => {
|
|
|
34
27
|
});
|
|
35
28
|
}
|
|
36
29
|
};
|
|
37
|
-
exports.onMessage = onMessage;
|
|
38
30
|
const onExit = (worker, code, signal) => {
|
|
39
31
|
if (!constants_1.LINKED && worker.exitedAfterDisconnect === false) {
|
|
40
32
|
listeners_1.logger.error({
|
|
@@ -78,11 +70,6 @@ const startMaster = (service) => {
|
|
|
78
70
|
if (service.deterministicVary) {
|
|
79
71
|
process.env.DETERMINISTIC_VARY = 'true';
|
|
80
72
|
}
|
|
81
|
-
// Set up the master-side Prometheus aggregator so workers can request a
|
|
82
|
-
// merged, monotonic /metrics view across the whole cluster over IPC.
|
|
83
|
-
if (numWorkers > 1) {
|
|
84
|
-
(0, clusterMetricsAggregator_1.initMasterAggregatorRegistry)();
|
|
85
|
-
}
|
|
86
73
|
// Setup dubugger
|
|
87
74
|
if (constants_1.LINKED) {
|
|
88
75
|
cluster_1.default.setupMaster({ inspectPort: constants_1.INSPECT_DEBUGGER_PORT });
|
|
@@ -95,7 +82,7 @@ const startMaster = (service) => {
|
|
|
95
82
|
}
|
|
96
83
|
cluster_1.default.on('online', onOnline);
|
|
97
84
|
cluster_1.default.on('exit', onExit);
|
|
98
|
-
cluster_1.default.on('message',
|
|
85
|
+
cluster_1.default.on('message', onMessage);
|
|
99
86
|
process.on('SIGINT', handleSignal(SIGINT_TIMEOUT_S));
|
|
100
87
|
process.on('SIGTERM', handleSignal(shutdownTimeout));
|
|
101
88
|
};
|
|
@@ -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.startWorker =
|
|
6
|
+
exports.startWorker = void 0;
|
|
7
7
|
const diagnostics_nodejs_1 = require("@vtex/diagnostics-nodejs");
|
|
8
8
|
const http_1 = require("http");
|
|
9
9
|
const koa_1 = __importDefault(require("koa"));
|
|
@@ -14,7 +14,6 @@ const constants_1 = require("../../constants");
|
|
|
14
14
|
const loaders_1 = require("../loaders");
|
|
15
15
|
const console_1 = require("../logger/console");
|
|
16
16
|
const loggerTypes_1 = require("../logger/loggerTypes");
|
|
17
|
-
const clusterMetricsAggregator_1 = require("../metrics/clusterMetricsAggregator");
|
|
18
17
|
const otelRequestMetricsMiddleware_1 = require("../metrics/otelRequestMetricsMiddleware");
|
|
19
18
|
const requestMetricsMiddleware_1 = require("../metrics/requestMetricsMiddleware");
|
|
20
19
|
const TracerSingleton_1 = require("../tracing/TracerSingleton");
|
|
@@ -56,12 +55,6 @@ const onMessage = (service) => (message) => {
|
|
|
56
55
|
else if ((0, statusTrack_1.isStatusTrack)(message)) {
|
|
57
56
|
(0, statusTrack_1.trackStatus)();
|
|
58
57
|
}
|
|
59
|
-
else if ((0, clusterMetricsAggregator_1.isAggMetricsResponse)(message)) {
|
|
60
|
-
(0, clusterMetricsAggregator_1.handleMasterMetricsResponse)(message);
|
|
61
|
-
}
|
|
62
|
-
else if ((0, clusterMetricsAggregator_1.isPromClientMessage)(message)) {
|
|
63
|
-
// Handled by prom-client's own worker listener; ignore here.
|
|
64
|
-
}
|
|
65
58
|
else {
|
|
66
59
|
listeners_1.logger.warn({
|
|
67
60
|
content: message,
|
|
@@ -70,7 +63,6 @@ const onMessage = (service) => (message) => {
|
|
|
70
63
|
});
|
|
71
64
|
}
|
|
72
65
|
};
|
|
73
|
-
exports.onMessage = onMessage;
|
|
74
66
|
const createAppHttpHandlers = ({ config: { routes, clients } }, serviceJSON, globalLimiter) => {
|
|
75
67
|
if (routes && clients) {
|
|
76
68
|
return Object.keys(routes).reduce((acc, routeId) => {
|
|
@@ -162,17 +154,12 @@ const startWorker = (serviceJSON) => {
|
|
|
162
154
|
var _a, _b;
|
|
163
155
|
(0, listeners_1.addProcessListeners)();
|
|
164
156
|
const tracer = TracerSingleton_1.TracerSingleton.getTracer();
|
|
165
|
-
// In multi-worker mode install prom-client's worker-side cluster responder so
|
|
166
|
-
// the master can collect this worker's registry for the aggregated /metrics.
|
|
167
|
-
if (serviceJSON.workers > 1) {
|
|
168
|
-
(0, clusterMetricsAggregator_1.ensureWorkerAggregatorRegistry)();
|
|
169
|
-
}
|
|
170
157
|
const app = new koa_1.default();
|
|
171
158
|
app.proxy = true;
|
|
172
159
|
app
|
|
173
160
|
.use(error_1.error)
|
|
174
161
|
.use(diagnostics_nodejs_1.Instrumentation.Middlewares.ContextMiddlewares.Koa.ContextPropagationMiddleware())
|
|
175
|
-
.use((0, middlewares_1.prometheusLoggerMiddleware)(
|
|
162
|
+
.use((0, middlewares_1.prometheusLoggerMiddleware)())
|
|
176
163
|
.use((0, tracingMiddlewares_1.addTracingMiddleware)(tracer))
|
|
177
164
|
.use((0, requestMetricsMiddleware_1.addRequestMetricsMiddleware)())
|
|
178
165
|
.use((0, otelRequestMetricsMiddleware_1.addOtelRequestMetricsMiddleware)())
|
|
@@ -197,7 +184,7 @@ const startWorker = (serviceJSON) => {
|
|
|
197
184
|
]
|
|
198
185
|
.filter(x => x != null)
|
|
199
186
|
// TODO: Fix ramda typings. Apparently there was an update that broke things
|
|
200
|
-
.reduce(
|
|
187
|
+
.reduce(ramda_1.mergeDeepRight);
|
|
201
188
|
if (httpHandlers === null || httpHandlers === void 0 ? void 0 : httpHandlers.pub) {
|
|
202
189
|
const publicHandlersRouter = (0, router_2.routerFromPublicHttpHandlers)(httpHandlers.pub);
|
|
203
190
|
app.use(publicHandlersRouter);
|
|
@@ -206,7 +193,7 @@ const startWorker = (serviceJSON) => {
|
|
|
206
193
|
const privateHandlersRouter = routerFromPrivateHttpHandlers(httpHandlers.pvt);
|
|
207
194
|
app.use(privateHandlersRouter.routes());
|
|
208
195
|
}
|
|
209
|
-
process.on('message',
|
|
196
|
+
process.on('message', onMessage(serviceJSON));
|
|
210
197
|
return app;
|
|
211
198
|
};
|
|
212
199
|
exports.startWorker = startWorker;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ServiceContext } from '../typings';
|
|
2
2
|
export declare function recorderMiddleware(ctx: ServiceContext, next: () => Promise<void>): Promise<void>;
|
|
3
3
|
export declare const addMetricsLoggerMiddleware: () => (ctx: ServiceContext, next: () => Promise<void>) => Promise<void>;
|
|
4
|
-
export declare const prometheusLoggerMiddleware: (
|
|
4
|
+
export declare const prometheusLoggerMiddleware: () => (ctx: ServiceContext, next: () => Promise<void>) => Promise<void>;
|
|
@@ -4,7 +4,6 @@ exports.prometheusLoggerMiddleware = exports.addMetricsLoggerMiddleware = export
|
|
|
4
4
|
const prom_client_1 = require("prom-client");
|
|
5
5
|
const constants_1 = require("../../../../constants");
|
|
6
6
|
const metricsLogger_1 = require("../../../logger/metricsLogger");
|
|
7
|
-
const clusterMetricsAggregator_1 = require("../../../metrics/clusterMetricsAggregator");
|
|
8
7
|
const EventLoopLagMeasurer_1 = require("../../../tracing/metrics/measurers/EventLoopLagMeasurer");
|
|
9
8
|
const recorder_1 = require("../utils/recorder");
|
|
10
9
|
async function recorderMiddleware(ctx, next) {
|
|
@@ -23,14 +22,10 @@ const addMetricsLoggerMiddleware = () => {
|
|
|
23
22
|
};
|
|
24
23
|
};
|
|
25
24
|
exports.addMetricsLoggerMiddleware = addMetricsLoggerMiddleware;
|
|
26
|
-
const prometheusLoggerMiddleware = (
|
|
25
|
+
const prometheusLoggerMiddleware = () => {
|
|
27
26
|
(0, prom_client_1.collectDefaultMetrics)();
|
|
28
27
|
const eventLoopLagMeasurer = new EventLoopLagMeasurer_1.EventLoopLagMeasurer();
|
|
29
28
|
eventLoopLagMeasurer.start();
|
|
30
|
-
// In multi-worker mode each worker holds only its own registry, so /metrics
|
|
31
|
-
// must serve the cluster-wide aggregate requested from the master over IPC.
|
|
32
|
-
// In single-worker mode (LINKED / workers:1) the local registry is complete.
|
|
33
|
-
const isMultiWorker = workers > 1;
|
|
34
29
|
return async (ctx, next) => {
|
|
35
30
|
if (ctx.request.path !== '/metrics') {
|
|
36
31
|
return next();
|
|
@@ -41,7 +36,7 @@ const prometheusLoggerMiddleware = (workers = 1) => {
|
|
|
41
36
|
}
|
|
42
37
|
await eventLoopLagMeasurer.updateInstrumentsAndReset();
|
|
43
38
|
ctx.set('Content-Type', prom_client_1.register.contentType);
|
|
44
|
-
ctx.body =
|
|
39
|
+
ctx.body = await prom_client_1.register.metrics();
|
|
45
40
|
ctx.status = 200;
|
|
46
41
|
};
|
|
47
42
|
};
|
|
@@ -26,22 +26,11 @@ exports.statusTrackHandler = statusTrackHandler;
|
|
|
26
26
|
const trackStatus = () => {
|
|
27
27
|
// Update diagnostics metrics (gauges for HTTP agent stats)
|
|
28
28
|
HttpAgentSingleton_1.HttpAgentSingleton.updateHttpAgentMetrics();
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
// Flushing resets the metric accumulators, the CPU usage baseline and the
|
|
30
|
+
// incoming request stats, so it must keep running even though nothing
|
|
31
|
+
// consumes the returned metrics anymore.
|
|
32
|
+
global.metrics.statusTrack();
|
|
33
33
|
};
|
|
34
34
|
exports.trackStatus = trackStatus;
|
|
35
35
|
const broadcastStatusTrack = () => Object.values(cluster_1.default.workers).forEach(worker => worker === null || worker === void 0 ? void 0 : worker.send(STATUS_TRACK));
|
|
36
36
|
exports.broadcastStatusTrack = broadcastStatusTrack;
|
|
37
|
-
const logStatus = (status) => console.log(JSON.stringify({
|
|
38
|
-
__VTEX_IO_LOG: true,
|
|
39
|
-
account: constants_1.ACCOUNT,
|
|
40
|
-
app: constants_1.APP.ID,
|
|
41
|
-
isLink: constants_1.LINKED,
|
|
42
|
-
pid: process.pid,
|
|
43
|
-
production: constants_1.PRODUCTION,
|
|
44
|
-
status,
|
|
45
|
-
type: 'metric/status',
|
|
46
|
-
workspace: constants_1.WORKSPACE,
|
|
47
|
-
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtex/api",
|
|
3
|
-
"version": "7.4.
|
|
3
|
+
"version": "7.4.2-beta.1",
|
|
4
4
|
"description": "VTEX I/O API client",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -132,5 +132,6 @@
|
|
|
132
132
|
},
|
|
133
133
|
"resolutions": {
|
|
134
134
|
"@grpc/grpc-js": "^1.13.4"
|
|
135
|
-
}
|
|
135
|
+
},
|
|
136
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
136
137
|
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { Worker } from 'cluster';
|
|
2
|
-
import { AggregatorRegistry } from 'prom-client';
|
|
3
|
-
/**
|
|
4
|
-
* Cluster-wide Prometheus metrics aggregation.
|
|
5
|
-
*
|
|
6
|
-
* In multi-worker mode each cluster worker keeps its own prom-client default
|
|
7
|
-
* registry. Serving `/metrics` from a single (round-robin selected) worker
|
|
8
|
-
* therefore exposes only that worker's local counters, which makes Prometheus
|
|
9
|
-
* treat the per-scrape braid of independent counters as counter resets and
|
|
10
|
-
* inflates `rate()`/`increase()` by orders of magnitude.
|
|
11
|
-
*
|
|
12
|
-
* This module uses prom-client's `AggregatorRegistry` cluster IPC protocol so
|
|
13
|
-
* that the worker answering a scrape asks the master for a merged, monotonic
|
|
14
|
-
* view built from every worker's registry in one pass (no double-counting of
|
|
15
|
-
* the answering worker).
|
|
16
|
-
*/
|
|
17
|
-
/** Tagged IPC message: worker -> master, "please build the cluster aggregate". */
|
|
18
|
-
export declare const AGG_METRICS_REQ = "vtex-api:aggMetricsReq";
|
|
19
|
-
/** Tagged IPC message: master -> worker, carries the aggregate (or an error). */
|
|
20
|
-
export declare const AGG_METRICS_RES = "vtex-api:aggMetricsRes";
|
|
21
|
-
export interface AggMetricsReqMessage {
|
|
22
|
-
type: typeof AGG_METRICS_REQ;
|
|
23
|
-
id: number;
|
|
24
|
-
}
|
|
25
|
-
export interface AggMetricsResMessage {
|
|
26
|
-
type: typeof AGG_METRICS_RES;
|
|
27
|
-
id: number;
|
|
28
|
-
body?: string;
|
|
29
|
-
error?: string;
|
|
30
|
-
}
|
|
31
|
-
export declare const isAggMetricsRequest: (message: any) => message is AggMetricsReqMessage;
|
|
32
|
-
export declare const isAggMetricsResponse: (message: any) => message is AggMetricsResMessage;
|
|
33
|
-
/**
|
|
34
|
-
* prom-client's cluster protocol emits its own tagged IPC messages
|
|
35
|
-
* (`prom-client:getMetricsReq` / `prom-client:getMetricsRes`). They are handled
|
|
36
|
-
* by prom-client's own cluster listeners, so our `onMessage` handlers must
|
|
37
|
-
* ignore them instead of warning about "unknown" messages.
|
|
38
|
-
*/
|
|
39
|
-
export declare const isPromClientMessage: (message: any) => boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Constructs the master-side `AggregatorRegistry` (idempotent). Its constructor
|
|
42
|
-
* installs prom-client's master `cluster.on('message')` collector, which is
|
|
43
|
-
* what makes `clusterMetrics()` work. Must be called in the master before any
|
|
44
|
-
* worker can answer a scrape.
|
|
45
|
-
*/
|
|
46
|
-
export declare const initMasterAggregatorRegistry: () => AggregatorRegistry;
|
|
47
|
-
/**
|
|
48
|
-
* Handles an `AGG_METRICS_REQ` from a worker: builds the cluster aggregate and
|
|
49
|
-
* replies to that worker only. prom-client fans the request out to *all*
|
|
50
|
-
* connected workers (including the requester), so the requester is one of the N
|
|
51
|
-
* merged sources and is never double-counted.
|
|
52
|
-
*/
|
|
53
|
-
export declare const handleWorkerMetricsRequest: (worker: Worker, message: AggMetricsReqMessage) => Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Constructs the worker-side `AggregatorRegistry` (idempotent). Its constructor
|
|
56
|
-
* installs prom-client's worker `process.on('message')` responder that answers
|
|
57
|
-
* the master's `getMetricsReq` with this worker's registry JSON.
|
|
58
|
-
*/
|
|
59
|
-
export declare const ensureWorkerAggregatorRegistry: () => AggregatorRegistry;
|
|
60
|
-
/**
|
|
61
|
-
* Requests the cluster aggregate from the master over IPC and resolves with the
|
|
62
|
-
* merged exposition string. On timeout or any error it falls back to this
|
|
63
|
-
* worker's local `register.metrics()` so `/metrics` always answers promptly.
|
|
64
|
-
*/
|
|
65
|
-
export declare const requestAggregatedMetrics: () => Promise<string>;
|
|
66
|
-
/**
|
|
67
|
-
* Resolves the pending `requestAggregatedMetrics` promise correlated by id when
|
|
68
|
-
* the master replies. On error it falls back to the local registry.
|
|
69
|
-
*/
|
|
70
|
-
export declare const handleMasterMetricsResponse: (message: AggMetricsResMessage) => void;
|
|
71
|
-
/** Test-only helper to reset module state between test cases. */
|
|
72
|
-
export declare const __resetForTests: () => void;
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.__resetForTests = exports.handleMasterMetricsResponse = exports.requestAggregatedMetrics = exports.ensureWorkerAggregatorRegistry = exports.handleWorkerMetricsRequest = exports.initMasterAggregatorRegistry = exports.isPromClientMessage = exports.isAggMetricsResponse = exports.isAggMetricsRequest = exports.AGG_METRICS_RES = exports.AGG_METRICS_REQ = void 0;
|
|
4
|
-
const prom_client_1 = require("prom-client");
|
|
5
|
-
const listeners_1 = require("../worker/listeners");
|
|
6
|
-
/**
|
|
7
|
-
* Cluster-wide Prometheus metrics aggregation.
|
|
8
|
-
*
|
|
9
|
-
* In multi-worker mode each cluster worker keeps its own prom-client default
|
|
10
|
-
* registry. Serving `/metrics` from a single (round-robin selected) worker
|
|
11
|
-
* therefore exposes only that worker's local counters, which makes Prometheus
|
|
12
|
-
* treat the per-scrape braid of independent counters as counter resets and
|
|
13
|
-
* inflates `rate()`/`increase()` by orders of magnitude.
|
|
14
|
-
*
|
|
15
|
-
* This module uses prom-client's `AggregatorRegistry` cluster IPC protocol so
|
|
16
|
-
* that the worker answering a scrape asks the master for a merged, monotonic
|
|
17
|
-
* view built from every worker's registry in one pass (no double-counting of
|
|
18
|
-
* the answering worker).
|
|
19
|
-
*/
|
|
20
|
-
/** Tagged IPC message: worker -> master, "please build the cluster aggregate". */
|
|
21
|
-
exports.AGG_METRICS_REQ = 'vtex-api:aggMetricsReq';
|
|
22
|
-
/** Tagged IPC message: master -> worker, carries the aggregate (or an error). */
|
|
23
|
-
exports.AGG_METRICS_RES = 'vtex-api:aggMetricsRes';
|
|
24
|
-
/**
|
|
25
|
-
* Upper bound for a worker waiting on the master aggregate. prom-client's own
|
|
26
|
-
* `clusterMetrics()` has a 5s timeout collecting worker registries; we guard
|
|
27
|
-
* slightly above that and fall back to the local registry on expiry so that
|
|
28
|
-
* `/metrics` never hangs or returns empty.
|
|
29
|
-
*/
|
|
30
|
-
const AGG_METRICS_TIMEOUT_MS = 6000;
|
|
31
|
-
const isAggMetricsRequest = (message) => (message === null || message === void 0 ? void 0 : message.type) === exports.AGG_METRICS_REQ && typeof (message === null || message === void 0 ? void 0 : message.id) === 'number';
|
|
32
|
-
exports.isAggMetricsRequest = isAggMetricsRequest;
|
|
33
|
-
const isAggMetricsResponse = (message) => (message === null || message === void 0 ? void 0 : message.type) === exports.AGG_METRICS_RES && typeof (message === null || message === void 0 ? void 0 : message.id) === 'number';
|
|
34
|
-
exports.isAggMetricsResponse = isAggMetricsResponse;
|
|
35
|
-
/**
|
|
36
|
-
* prom-client's cluster protocol emits its own tagged IPC messages
|
|
37
|
-
* (`prom-client:getMetricsReq` / `prom-client:getMetricsRes`). They are handled
|
|
38
|
-
* by prom-client's own cluster listeners, so our `onMessage` handlers must
|
|
39
|
-
* ignore them instead of warning about "unknown" messages.
|
|
40
|
-
*/
|
|
41
|
-
const isPromClientMessage = (message) => typeof (message === null || message === void 0 ? void 0 : message.type) === 'string' && message.type.startsWith('prom-client:');
|
|
42
|
-
exports.isPromClientMessage = isPromClientMessage;
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
// Master side
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
let masterAggregatorRegistry;
|
|
47
|
-
/**
|
|
48
|
-
* Constructs the master-side `AggregatorRegistry` (idempotent). Its constructor
|
|
49
|
-
* installs prom-client's master `cluster.on('message')` collector, which is
|
|
50
|
-
* what makes `clusterMetrics()` work. Must be called in the master before any
|
|
51
|
-
* worker can answer a scrape.
|
|
52
|
-
*/
|
|
53
|
-
const initMasterAggregatorRegistry = () => {
|
|
54
|
-
if (!masterAggregatorRegistry) {
|
|
55
|
-
masterAggregatorRegistry = new prom_client_1.AggregatorRegistry();
|
|
56
|
-
}
|
|
57
|
-
return masterAggregatorRegistry;
|
|
58
|
-
};
|
|
59
|
-
exports.initMasterAggregatorRegistry = initMasterAggregatorRegistry;
|
|
60
|
-
/**
|
|
61
|
-
* Handles an `AGG_METRICS_REQ` from a worker: builds the cluster aggregate and
|
|
62
|
-
* replies to that worker only. prom-client fans the request out to *all*
|
|
63
|
-
* connected workers (including the requester), so the requester is one of the N
|
|
64
|
-
* merged sources and is never double-counted.
|
|
65
|
-
*/
|
|
66
|
-
const handleWorkerMetricsRequest = async (worker, message) => {
|
|
67
|
-
var _a;
|
|
68
|
-
const aggregator = (0, exports.initMasterAggregatorRegistry)();
|
|
69
|
-
try {
|
|
70
|
-
const body = await aggregator.clusterMetrics();
|
|
71
|
-
worker.send({ type: exports.AGG_METRICS_RES, id: message.id, body });
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
worker.send({ type: exports.AGG_METRICS_RES, id: message.id, error: (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err) });
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
exports.handleWorkerMetricsRequest = handleWorkerMetricsRequest;
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
// Worker side
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
let workerAggregatorRegistry;
|
|
82
|
-
const pendingRequests = new Map();
|
|
83
|
-
let requestCounter = 0;
|
|
84
|
-
/**
|
|
85
|
-
* Constructs the worker-side `AggregatorRegistry` (idempotent). Its constructor
|
|
86
|
-
* installs prom-client's worker `process.on('message')` responder that answers
|
|
87
|
-
* the master's `getMetricsReq` with this worker's registry JSON.
|
|
88
|
-
*/
|
|
89
|
-
const ensureWorkerAggregatorRegistry = () => {
|
|
90
|
-
if (!workerAggregatorRegistry) {
|
|
91
|
-
workerAggregatorRegistry = new prom_client_1.AggregatorRegistry();
|
|
92
|
-
}
|
|
93
|
-
return workerAggregatorRegistry;
|
|
94
|
-
};
|
|
95
|
-
exports.ensureWorkerAggregatorRegistry = ensureWorkerAggregatorRegistry;
|
|
96
|
-
/**
|
|
97
|
-
* Requests the cluster aggregate from the master over IPC and resolves with the
|
|
98
|
-
* merged exposition string. On timeout or any error it falls back to this
|
|
99
|
-
* worker's local `register.metrics()` so `/metrics` always answers promptly.
|
|
100
|
-
*/
|
|
101
|
-
const requestAggregatedMetrics = async () => {
|
|
102
|
-
if (typeof process.send !== 'function') {
|
|
103
|
-
return prom_client_1.register.metrics();
|
|
104
|
-
}
|
|
105
|
-
const id = requestCounter++;
|
|
106
|
-
return new Promise((resolve) => {
|
|
107
|
-
var _a;
|
|
108
|
-
const timer = setTimeout(() => {
|
|
109
|
-
pendingRequests.delete(id);
|
|
110
|
-
listeners_1.logger.warn({
|
|
111
|
-
message: 'Timed out waiting for aggregated cluster metrics; falling back to local registry',
|
|
112
|
-
pid: process.pid,
|
|
113
|
-
});
|
|
114
|
-
prom_client_1.register.metrics().then(resolve).catch(() => resolve(''));
|
|
115
|
-
}, AGG_METRICS_TIMEOUT_MS);
|
|
116
|
-
pendingRequests.set(id, { resolve, timer });
|
|
117
|
-
try {
|
|
118
|
-
process.send({ type: exports.AGG_METRICS_REQ, id });
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
clearTimeout(timer);
|
|
122
|
-
pendingRequests.delete(id);
|
|
123
|
-
listeners_1.logger.warn({
|
|
124
|
-
content: (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err),
|
|
125
|
-
message: 'Failed to request aggregated cluster metrics; falling back to local registry',
|
|
126
|
-
pid: process.pid,
|
|
127
|
-
});
|
|
128
|
-
prom_client_1.register.metrics().then(resolve).catch(() => resolve(''));
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
};
|
|
132
|
-
exports.requestAggregatedMetrics = requestAggregatedMetrics;
|
|
133
|
-
/**
|
|
134
|
-
* Resolves the pending `requestAggregatedMetrics` promise correlated by id when
|
|
135
|
-
* the master replies. On error it falls back to the local registry.
|
|
136
|
-
*/
|
|
137
|
-
const handleMasterMetricsResponse = (message) => {
|
|
138
|
-
const pending = pendingRequests.get(message.id);
|
|
139
|
-
if (!pending) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
pendingRequests.delete(message.id);
|
|
143
|
-
clearTimeout(pending.timer);
|
|
144
|
-
if (message.error != null || message.body == null) {
|
|
145
|
-
listeners_1.logger.warn({
|
|
146
|
-
content: message.error,
|
|
147
|
-
message: 'Master failed to aggregate cluster metrics; falling back to local registry',
|
|
148
|
-
pid: process.pid,
|
|
149
|
-
});
|
|
150
|
-
prom_client_1.register.metrics().then(pending.resolve).catch(() => pending.resolve(''));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
pending.resolve(message.body);
|
|
154
|
-
};
|
|
155
|
-
exports.handleMasterMetricsResponse = handleMasterMetricsResponse;
|
|
156
|
-
/** Test-only helper to reset module state between test cases. */
|
|
157
|
-
const __resetForTests = () => {
|
|
158
|
-
masterAggregatorRegistry = undefined;
|
|
159
|
-
workerAggregatorRegistry = undefined;
|
|
160
|
-
pendingRequests.forEach((p) => clearTimeout(p.timer));
|
|
161
|
-
pendingRequests.clear();
|
|
162
|
-
requestCounter = 0;
|
|
163
|
-
};
|
|
164
|
-
exports.__resetForTests = __resetForTests;
|