@adalo/metrics 0.0.0-staging.17 → 0.0.0-staging.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.
Files changed (31) hide show
  1. package/lib/metrics/httpMetricsRedisCollector.d.ts.map +1 -1
  2. package/lib/metrics/httpMetricsRedisCollector.js +23 -1
  3. package/lib/metrics/httpMetricsRedisCollector.js.map +1 -1
  4. package/lib/metrics/httpMetricsRedisRecorder.d.ts +0 -3
  5. package/lib/metrics/httpMetricsRedisRecorder.d.ts.map +1 -1
  6. package/lib/metrics/httpMetricsRedisRecorder.js +0 -32
  7. package/lib/metrics/httpMetricsRedisRecorder.js.map +1 -1
  8. package/lib/metrics/httpMetricsRedisStore.d.ts.map +1 -1
  9. package/lib/metrics/httpMetricsRedisStore.js +27 -1
  10. package/lib/metrics/httpMetricsRedisStore.js.map +1 -1
  11. package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -1
  12. package/lib/metrics/metricsDatabaseClient.js +4 -3
  13. package/lib/metrics/metricsDatabaseClient.js.map +1 -1
  14. package/lib/metrics/metricsProcessTypeUtils.d.ts +44 -13
  15. package/lib/metrics/metricsProcessTypeUtils.d.ts.map +1 -1
  16. package/lib/metrics/metricsProcessTypeUtils.js +59 -10
  17. package/lib/metrics/metricsProcessTypeUtils.js.map +1 -1
  18. package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -1
  19. package/lib/metrics/metricsQueueRedisClient.js +3 -2
  20. package/lib/metrics/metricsQueueRedisClient.js.map +1 -1
  21. package/lib/metrics/metricsRedisClient.d.ts.map +1 -1
  22. package/lib/metrics/metricsRedisClient.js +5 -3
  23. package/lib/metrics/metricsRedisClient.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/metrics/httpMetricsRedisCollector.js +26 -1
  26. package/src/metrics/httpMetricsRedisRecorder.js +1 -43
  27. package/src/metrics/httpMetricsRedisStore.js +73 -4
  28. package/src/metrics/metricsDatabaseClient.js +6 -3
  29. package/src/metrics/metricsProcessTypeUtils.js +72 -10
  30. package/src/metrics/metricsQueueRedisClient.js +5 -2
  31. package/src/metrics/metricsRedisClient.js +11 -6
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisCollector.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisCollector.js"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;qBAfW,OAAO,OAAO,EAAE,WAAW;;;;;;;;;;;;;;mBAoErC;IAhCC,8BAKE;CAgDL"}
1
+ {"version":3,"file":"httpMetricsRedisCollector.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisCollector.js"],"names":[],"mappings":"AAeA;;;;;;;GAOG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;qBAfW,OAAO,OAAO,EAAE,WAAW;;;;;;;;;;;;;;mBAoErC;IAhCC,8BAKE;CA6DL"}
@@ -1,11 +1,22 @@
1
1
  "use strict";
2
2
 
3
+ const cluster = require('cluster');
3
4
  const {
4
5
  BaseMetricsClient
5
6
  } = require('./baseMetricsClient');
6
7
  const {
7
8
  HttpMetricsRedisStore
8
9
  } = require('./httpMetricsRedisStore');
10
+ function clusterWorkerTag() {
11
+ try {
12
+ if (cluster.isWorker && cluster.worker) {
13
+ return ` cluster_worker_id=${cluster.worker.id}`;
14
+ }
15
+ } catch (_) {
16
+ /* ignore */
17
+ }
18
+ return '';
19
+ }
9
20
 
10
21
  /**
11
22
  * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),
@@ -74,10 +85,21 @@ class HttpMetricsRedisCollector extends BaseMetricsClient {
74
85
  * @returns {Promise<void>}
75
86
  */
76
87
  pushMetrics = async () => {
88
+ const {
89
+ pid
90
+ } = process;
91
+ const clusterTag = clusterWorkerTag();
92
+ // --- TEMP_HTTP_METRICS_DIAG: remove when done debugging ---
93
+ console.warn(`[TEMP_HTTP_METRICS_DIAG] http_metrics_publish_start pid=${pid}${clusterTag} dyno_id=${this.dynoId} process_type=${this.processType} app=${this.appName}`);
94
+ // --- end TEMP_HTTP_METRICS_DIAG ---
77
95
  if (this._store && this.countersFunctions?.app_requests_total && this.countersFunctions?.app_requests_total_duration) {
78
96
  await this._store.flushToCounters((labels, value) => this.countersFunctions.app_requests_total(labels, value), (labels, value) => this.countersFunctions.app_requests_total_duration(labels, value));
79
97
  }
80
- return this._pushMetrics();
98
+ const out = await this._pushMetrics();
99
+ // --- TEMP_HTTP_METRICS_DIAG: remove when done debugging ---
100
+ console.warn(`[TEMP_HTTP_METRICS_DIAG] http_metrics_publish_finished pid=${pid}${clusterTag} dyno_id=${this.dynoId} (registry POST to VM-agent / import completed for this tick)`);
101
+ // --- end TEMP_HTTP_METRICS_DIAG ---
102
+ return out;
81
103
  };
82
104
  }
83
105
  module.exports = {
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisCollector.js","names":["BaseMetricsClient","require","HttpMetricsRedisStore","HttpMetricsRedisCollector","constructor","config","redisClient","Error","blockNodeDefaultMetrics","keyProcessType","redisProcessTypeForKeys","process","env","METRICS_HTTP_REDIS_KEY_PROCESS_TYPE","defaultLabelsWithoutDynoId","app","appName","process_type","_store","processType","ttlSec","createCounter","name","help","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","pushMetrics","countersFunctions","app_requests_total","app_requests_total_duration","flushToCounters","labels","value","_pushMetrics","module","exports"],"sources":["../../src/metrics/httpMetricsRedisCollector.js"],"sourcesContent":["const { BaseMetricsClient } = require('./baseMetricsClient')\nconst { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\n/**\n * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),\n * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).\n * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).\n * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).\n *\n * @extends BaseMetricsClient\n */\nclass HttpMetricsRedisCollector extends BaseMetricsClient {\n /**\n * @param {Object} [config]\n * @param {import('redis').RedisClient} config.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} [config.appName] Application name (defaults per {@link BaseMetricsClient})\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Label `process_type` on push (default from env / base)\n * @param {boolean} [config.enabled] Enable collection and push\n * @param {boolean} [config.logValues] Log metric JSON to console\n * @param {string} [config.pushgatewayUrl] VM-agent import URL\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64)\n * @param {number} [config.intervalSec] Push interval (seconds)\n * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported\n * @param {function} [config.startupValidation] Run before first push\n * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent\n * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)\n * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)\n */\n constructor(config = {}) {\n const { redisClient } = config\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisCollector: redisClient is required')\n }\n\n super({\n ...config,\n blockNodeDefaultMetrics: true,\n })\n\n const keyProcessType =\n config.redisProcessTypeForKeys ||\n process.env.METRICS_HTTP_REDIS_KEY_PROCESS_TYPE ||\n 'web'\n\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: keyProcessType,\n }\n\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName: this.appName,\n processType: keyProcessType,\n ttlSec: config.ttlSec,\n })\n\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n\n /**\n * Drains Redis into counters, then runs gauge updates and VM-agent push ({@link BaseMetricsClient#_pushMetrics}).\n * @returns {Promise<void>}\n */\n pushMetrics = async () => {\n if (\n this._store &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n await this._store.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n }\n return this._pushMetrics()\n }\n}\n\nmodule.exports = { HttpMetricsRedisCollector }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAkB,CAAC,GAAGC,OAAO,CAAC,qBAAqB,CAAC;AAC5D,MAAM;EAAEC;AAAsB,CAAC,GAAGD,OAAO,CAAC,yBAAyB,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAME,yBAAyB,SAASH,iBAAiB,CAAC;EACxD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEI,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,MAAM;MAAEC;IAAY,CAAC,GAAGD,MAAM;IAC9B,IAAIC,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAIC,KAAK,CAAC,oDAAoD,CAAC;IACvE;IAEA,KAAK,CAAC;MACJ,GAAGF,MAAM;MACTG,uBAAuB,EAAE;IAC3B,CAAC,CAAC;IAEF,MAAMC,cAAc,GAClBJ,MAAM,CAACK,uBAAuB,IAC9BC,OAAO,CAACC,GAAG,CAACC,mCAAmC,IAC/C,KAAK;IAEP,IAAI,CAACC,0BAA0B,GAAG;MAChCC,GAAG,EAAE,IAAI,CAACC,OAAO;MACjBC,YAAY,EAAER;IAChB,CAAC;IAED,IAAI,CAACS,MAAM,GAAG,IAAIhB,qBAAqB,CAAC;MACtCI,WAAW;MACXU,OAAO,EAAE,IAAI,CAACA,OAAO;MACrBG,WAAW,EAAEV,cAAc;MAC3BW,MAAM,EAAEf,MAAM,CAACe;IACjB,CAAC,CAAC;IAEF,IAAI,CAACC,aAAa,CAAC;MACjBC,IAAI,EAAE,oBAAoB;MAC1BC,IAAI,EAAE,+BAA+B;MACrCC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;IAEF,IAAI,CAACL,aAAa,CAAC;MACjBC,IAAI,EAAE,6BAA6B;MACnCC,IAAI,EAAE,iDAAiD;MACvDC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;EACEC,WAAW,GAAG,MAAAA,CAAA,KAAY;IACxB,IACE,IAAI,CAACT,MAAM,IACX,IAAI,CAACU,iBAAiB,EAAEC,kBAAkB,IAC1C,IAAI,CAACD,iBAAiB,EAAEE,2BAA2B,EACnD;MACA,MAAM,IAAI,CAACZ,MAAM,CAACa,eAAe,CAC/B,CAACC,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACC,kBAAkB,CAACG,MAAM,EAAEC,KAAK,CAAC,EAC1D,CAACD,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACE,2BAA2B,CAACE,MAAM,EAAEC,KAAK,CACpE,CAAC;IACH;IACA,OAAO,IAAI,CAACC,YAAY,CAAC,CAAC;EAC5B,CAAC;AACH;AAEAC,MAAM,CAACC,OAAO,GAAG;EAAEjC;AAA0B,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisCollector.js","names":["cluster","require","BaseMetricsClient","HttpMetricsRedisStore","clusterWorkerTag","isWorker","worker","id","_","HttpMetricsRedisCollector","constructor","config","redisClient","Error","blockNodeDefaultMetrics","keyProcessType","redisProcessTypeForKeys","process","env","METRICS_HTTP_REDIS_KEY_PROCESS_TYPE","defaultLabelsWithoutDynoId","app","appName","process_type","_store","processType","ttlSec","createCounter","name","help","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","pushMetrics","pid","clusterTag","console","warn","dynoId","countersFunctions","app_requests_total","app_requests_total_duration","flushToCounters","labels","value","out","_pushMetrics","module","exports"],"sources":["../../src/metrics/httpMetricsRedisCollector.js"],"sourcesContent":["const cluster = require('cluster')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\nconst { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\nfunction clusterWorkerTag() {\n try {\n if (cluster.isWorker && cluster.worker) {\n return ` cluster_worker_id=${cluster.worker.id}`\n }\n } catch (_) {\n /* ignore */\n }\n return ''\n}\n\n/**\n * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),\n * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).\n * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).\n * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).\n *\n * @extends BaseMetricsClient\n */\nclass HttpMetricsRedisCollector extends BaseMetricsClient {\n /**\n * @param {Object} [config]\n * @param {import('redis').RedisClient} config.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} [config.appName] Application name (defaults per {@link BaseMetricsClient})\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Label `process_type` on push (default from env / base)\n * @param {boolean} [config.enabled] Enable collection and push\n * @param {boolean} [config.logValues] Log metric JSON to console\n * @param {string} [config.pushgatewayUrl] VM-agent import URL\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64)\n * @param {number} [config.intervalSec] Push interval (seconds)\n * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported\n * @param {function} [config.startupValidation] Run before first push\n * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent\n * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)\n * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)\n */\n constructor(config = {}) {\n const { redisClient } = config\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisCollector: redisClient is required')\n }\n\n super({\n ...config,\n blockNodeDefaultMetrics: true,\n })\n\n const keyProcessType =\n config.redisProcessTypeForKeys ||\n process.env.METRICS_HTTP_REDIS_KEY_PROCESS_TYPE ||\n 'web'\n\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: keyProcessType,\n }\n\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName: this.appName,\n processType: keyProcessType,\n ttlSec: config.ttlSec,\n })\n\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n\n /**\n * Drains Redis into counters, then runs gauge updates and VM-agent push ({@link BaseMetricsClient#_pushMetrics}).\n * @returns {Promise<void>}\n */\n pushMetrics = async () => {\n const { pid } = process\n const clusterTag = clusterWorkerTag()\n // --- TEMP_HTTP_METRICS_DIAG: remove when done debugging ---\n console.warn(\n `[TEMP_HTTP_METRICS_DIAG] http_metrics_publish_start pid=${pid}${clusterTag} dyno_id=${this.dynoId} process_type=${this.processType} app=${this.appName}`\n )\n // --- end TEMP_HTTP_METRICS_DIAG ---\n if (\n this._store &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n await this._store.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n }\n const out = await this._pushMetrics()\n // --- TEMP_HTTP_METRICS_DIAG: remove when done debugging ---\n console.warn(\n `[TEMP_HTTP_METRICS_DIAG] http_metrics_publish_finished pid=${pid}${clusterTag} dyno_id=${this.dynoId} (registry POST to VM-agent / import completed for this tick)`\n )\n // --- end TEMP_HTTP_METRICS_DIAG ---\n return out\n }\n}\n\nmodule.exports = { HttpMetricsRedisCollector }\n"],"mappings":"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","ignoreList":[]}
@@ -21,9 +21,6 @@ export class HttpMetricsRedisRecorder {
21
21
  processType: string;
22
22
  appName: string;
23
23
  _store: HttpMetricsRedisStore;
24
- _diagWindowStart: number;
25
- _diagWindowCount: number;
26
- _diagIntervalMs: number;
27
24
  /**
28
25
  * @param {Object} params
29
26
  * @param {string} params.method
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisRecorder.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAoB9B;IAdC,oBAA8B;IAC9B,gBAAsB;IACtB,8BAKE;IAGF,yBAAyB;IACzB,yBAAyB;IACzB,wBAA2B;IAI7B;;;;;;;;OAQG;IACH;QAP0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACU,KAAK;QACL,UAAU;QACX,QAAQ,EAAvB,MAAM;aAmChB;IAED;;;;;;;OAOG;IACH,kCAJW,OAAO,MAAM,EAAE,eAAe,OAC9B,OAAO,MAAM,EAAE,cAAc,0BAkCvC;CACF"}
1
+ {"version":3,"file":"httpMetricsRedisRecorder.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAc9B;IARC,oBAA8B;IAC9B,gBAAsB;IACtB,8BAKE;IAGJ;;;;;;;;OAQG;IACH;QAP0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACU,KAAK;QACL,UAAU;QACX,QAAQ,EAAvB,MAAM;aAWhB;IAED;;;;;;;OAOG;IACH,kCAJW,OAAO,MAAM,EAAE,eAAe,OAC9B,OAAO,MAAM,EAAE,cAAc,0BAkCvC;CACF"}
@@ -1,19 +1,8 @@
1
1
  "use strict";
2
2
 
3
- const cluster = require('cluster');
4
3
  const {
5
4
  HttpMetricsRedisStore
6
5
  } = require('./httpMetricsRedisStore');
7
- function clusterWorkerTag() {
8
- try {
9
- if (cluster.isWorker && cluster.worker) {
10
- return ` cluster_worker_id=${cluster.worker.id}`;
11
- }
12
- } catch (_) {
13
- /* ignore */
14
- }
15
- return '';
16
- }
17
6
 
18
7
  /**
19
8
  * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).
@@ -46,12 +35,6 @@ class HttpMetricsRedisRecorder {
46
35
  processType,
47
36
  ttlSec
48
37
  });
49
-
50
- // --- TEMP_HTTP_METRICS_DIAG: remove this block when cluster / multi-web coverage is verified ---
51
- this._diagWindowStart = 0;
52
- this._diagWindowCount = 0;
53
- this._diagIntervalMs = 5000;
54
- // --- end TEMP_HTTP_METRICS_DIAG ---
55
38
  }
56
39
 
57
40
  /**
@@ -71,21 +54,6 @@ class HttpMetricsRedisRecorder {
71
54
  databaseId = '',
72
55
  duration
73
56
  }) {
74
- // --- TEMP_HTTP_METRICS_DIAG: remove this block when cluster / multi-web coverage is verified ---
75
- this._diagWindowCount += 1;
76
- const now = Date.now();
77
- if (!this._diagWindowStart) {
78
- this._diagWindowStart = now;
79
- }
80
- if (now - this._diagWindowStart >= this._diagIntervalMs) {
81
- const pidTag = `pid=${process.pid}`;
82
- const clusterTag = clusterWorkerTag();
83
- console.warn(`[TEMP_HTTP_METRICS_DIAG] http_record_source ${pidTag}${clusterTag} process_type=${this.processType} app=${this.appName} batch_count=${this._diagWindowCount} (before Redis write)`);
84
- this._diagWindowStart = now;
85
- this._diagWindowCount = 0;
86
- }
87
- // --- end TEMP_HTTP_METRICS_DIAG ---
88
-
89
57
  this._store.record(method, route, status_code, appId, databaseId, duration);
90
58
  }
91
59
 
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisRecorder.js","names":["cluster","require","HttpMetricsRedisStore","clusterWorkerTag","isWorker","worker","id","_","HttpMetricsRedisRecorder","constructor","redisClient","appName","processType","ttlSec","Error","_store","_diagWindowStart","_diagWindowCount","_diagIntervalMs","trackHttpRequest","method","route","status_code","appId","databaseId","duration","now","Date","pidTag","process","pid","clusterTag","console","warn","record","trackHttpRequestMiddleware","req","res","next","start","on","path","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"sourcesContent":["const cluster = require('cluster')\nconst { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\nfunction clusterWorkerTag() {\n try {\n if (cluster.isWorker && cluster.worker) {\n return ` cluster_worker_id=${cluster.worker.id}`\n }\n } catch (_) {\n /* ignore */\n }\n return ''\n}\n\n/**\n * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).\n * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.\n *\n * @see HttpMetricsRedisStore\n */\nclass HttpMetricsRedisRecorder {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} opts.appName Application name (must match collector; typically `BUILD_APP_NAME`).\n * @param {string} opts.processType Logical process segment in Redis keys (e.g. `web`; must match collector’s key segment).\n * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisRecorder: redisClient is required')\n }\n this.processType = processType\n this.appName = appName\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName,\n processType,\n ttlSec,\n })\n\n // --- TEMP_HTTP_METRICS_DIAG: remove this block when cluster / multi-web coverage is verified ---\n this._diagWindowStart = 0\n this._diagWindowCount = 0\n this._diagIntervalMs = 5000\n // --- end TEMP_HTTP_METRICS_DIAG ---\n }\n\n /**\n * @param {Object} params\n * @param {string} params.method\n * @param {string} params.route\n * @param {number} params.status_code\n * @param {string} [params.appId]\n * @param {string} [params.databaseId]\n * @param {number} params.duration\n */\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n // --- TEMP_HTTP_METRICS_DIAG: remove this block when cluster / multi-web coverage is verified ---\n this._diagWindowCount += 1\n const now = Date.now()\n if (!this._diagWindowStart) {\n this._diagWindowStart = now\n }\n if (now - this._diagWindowStart >= this._diagIntervalMs) {\n const pidTag = `pid=${process.pid}`\n const clusterTag = clusterWorkerTag()\n console.warn(\n `[TEMP_HTTP_METRICS_DIAG] http_record_source ${pidTag}${clusterTag} process_type=${this.processType} app=${this.appName} batch_count=${this._diagWindowCount} (before Redis write)`\n )\n this._diagWindowStart = now\n this._diagWindowCount = 0\n }\n // --- end TEMP_HTTP_METRICS_DIAG ---\n\n this._store.record(\n method,\n route,\n status_code,\n appId,\n databaseId,\n duration\n )\n }\n\n /**\n * Express middleware: appends a `finish` listener and writes one aggregate row per request to Redis.\n * Does not check `METRICS_ENABLED`; the app should only mount this when HTTP Redis metrics should run.\n *\n * @param {import('http').IncomingMessage} req\n * @param {import('http').ServerResponse} res\n * @param {function} next\n */\n trackHttpRequestMiddleware = (req, res, next) => {\n if (req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { HttpMetricsRedisRecorder }\n"],"mappings":";;AAAA,MAAMA,OAAO,GAAGC,OAAO,CAAC,SAAS,CAAC;AAClC,MAAM;EAAEC;AAAsB,CAAC,GAAGD,OAAO,CAAC,yBAAyB,CAAC;AAEpE,SAASE,gBAAgBA,CAAA,EAAG;EAC1B,IAAI;IACF,IAAIH,OAAO,CAACI,QAAQ,IAAIJ,OAAO,CAACK,MAAM,EAAE;MACtC,OAAO,sBAAsBL,OAAO,CAACK,MAAM,CAACC,EAAE,EAAE;IAClD;EACF,CAAC,CAAC,OAAOC,CAAC,EAAE;IACV;EAAA;EAEF,OAAO,EAAE;AACX;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,wBAAwB,CAAC;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAAC;IAAEC,WAAW;IAAEC,OAAO;IAAEC,WAAW;IAAEC;EAAO,CAAC,EAAE;IACzD,IAAIH,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAII,KAAK,CAAC,mDAAmD,CAAC;IACtE;IACA,IAAI,CAACF,WAAW,GAAGA,WAAW;IAC9B,IAAI,CAACD,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACI,MAAM,GAAG,IAAIb,qBAAqB,CAAC;MACtCQ,WAAW;MACXC,OAAO;MACPC,WAAW;MACXC;IACF,CAAC,CAAC;;IAEF;IACA,IAAI,CAACG,gBAAgB,GAAG,CAAC;IACzB,IAAI,CAACC,gBAAgB,GAAG,CAAC;IACzB,IAAI,CAACC,eAAe,GAAG,IAAI;IAC3B;EACF;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEC,gBAAgBA,CAAC;IACfC,MAAM;IACNC,KAAK;IACLC,WAAW;IACXC,KAAK,GAAG,EAAE;IACVC,UAAU,GAAG,EAAE;IACfC;EACF,CAAC,EAAE;IACD;IACA,IAAI,CAACR,gBAAgB,IAAI,CAAC;IAC1B,MAAMS,GAAG,GAAGC,IAAI,CAACD,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,CAACV,gBAAgB,EAAE;MAC1B,IAAI,CAACA,gBAAgB,GAAGU,GAAG;IAC7B;IACA,IAAIA,GAAG,GAAG,IAAI,CAACV,gBAAgB,IAAI,IAAI,CAACE,eAAe,EAAE;MACvD,MAAMU,MAAM,GAAG,OAAOC,OAAO,CAACC,GAAG,EAAE;MACnC,MAAMC,UAAU,GAAG5B,gBAAgB,CAAC,CAAC;MACrC6B,OAAO,CAACC,IAAI,CACV,+CAA+CL,MAAM,GAAGG,UAAU,iBAAiB,IAAI,CAACnB,WAAW,QAAQ,IAAI,CAACD,OAAO,gBAAgB,IAAI,CAACM,gBAAgB,uBAC9J,CAAC;MACD,IAAI,CAACD,gBAAgB,GAAGU,GAAG;MAC3B,IAAI,CAACT,gBAAgB,GAAG,CAAC;IAC3B;IACA;;IAEA,IAAI,CAACF,MAAM,CAACmB,MAAM,CAChBd,MAAM,EACNC,KAAK,EACLC,WAAW,EACXC,KAAK,EACLC,UAAU,EACVC,QACF,CAAC;EACH;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEU,0BAA0B,GAAGA,CAACC,GAAG,EAAEC,GAAG,EAAEC,IAAI,KAAK;IAC/C,IAAIF,GAAG,CAAChB,MAAM,KAAK,SAAS,EAAE;MAC5BkB,IAAI,CAAC,CAAC;MACN;IACF;IAEA,MAAMC,KAAK,GAAGZ,IAAI,CAACD,GAAG,CAAC,CAAC;IACxBW,GAAG,CAACG,EAAE,CAAC,QAAQ,EAAE,MAAM;MACrB,MAAMnB,KAAK,GAAGe,GAAG,CAACf,KAAK,EAAEoB,IAAI,IAAIL,GAAG,CAACK,IAAI,IAAI,SAAS;MACtD,MAAMlB,KAAK,GACTa,GAAG,CAACM,MAAM,EAAEnB,KAAK,IAAIa,GAAG,CAACO,IAAI,EAAEpB,KAAK,IAAIa,GAAG,CAACQ,KAAK,EAAErB,KAAK,IAAI,EAAE;MAChE,MAAMC,UAAU,GACdY,GAAG,CAACM,MAAM,EAAElB,UAAU,IACtBY,GAAG,CAACO,IAAI,EAAEnB,UAAU,IACpBY,GAAG,CAACQ,KAAK,EAAEpB,UAAU,IACrBY,GAAG,CAACM,MAAM,EAAEG,YAAY,IACxBT,GAAG,CAACO,IAAI,EAAEE,YAAY,IACtBT,GAAG,CAACQ,KAAK,EAAEC,YAAY,IACvB,EAAE;MAEJ,IAAI,CAAC1B,gBAAgB,CAAC;QACpBC,MAAM,EAAEgB,GAAG,CAAChB,MAAM;QAClBC,KAAK;QACLC,WAAW,EAAEe,GAAG,CAACS,UAAU;QAC3BvB,KAAK;QACLC,UAAU;QACVC,QAAQ,EAAEE,IAAI,CAACD,GAAG,CAAC,CAAC,GAAGa;MACzB,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFD,IAAI,CAAC,CAAC;EACR,CAAC;AACH;AAEAS,MAAM,CAACC,OAAO,GAAG;EAAExC;AAAyB,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisRecorder.js","names":["HttpMetricsRedisStore","require","HttpMetricsRedisRecorder","constructor","redisClient","appName","processType","ttlSec","Error","_store","trackHttpRequest","method","route","status_code","appId","databaseId","duration","record","trackHttpRequestMiddleware","req","res","next","start","Date","now","on","path","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"sourcesContent":["const { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\n/**\n * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).\n * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.\n *\n * @see HttpMetricsRedisStore\n */\nclass HttpMetricsRedisRecorder {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} opts.appName Application name (must match collector; typically `BUILD_APP_NAME`).\n * @param {string} opts.processType Logical process segment in Redis keys (e.g. `web`; must match collector’s key segment).\n * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisRecorder: redisClient is required')\n }\n this.processType = processType\n this.appName = appName\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName,\n processType,\n ttlSec,\n })\n }\n\n /**\n * @param {Object} params\n * @param {string} params.method\n * @param {string} params.route\n * @param {number} params.status_code\n * @param {string} [params.appId]\n * @param {string} [params.databaseId]\n * @param {number} params.duration\n */\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n this._store.record(method, route, status_code, appId, databaseId, duration)\n }\n\n /**\n * Express middleware: appends a `finish` listener and writes one aggregate row per request to Redis.\n * Does not check `METRICS_ENABLED`; the app should only mount this when HTTP Redis metrics should run.\n *\n * @param {import('http').IncomingMessage} req\n * @param {import('http').ServerResponse} res\n * @param {function} next\n */\n trackHttpRequestMiddleware = (req, res, next) => {\n if (req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { HttpMetricsRedisRecorder }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAsB,CAAC,GAAGC,OAAO,CAAC,yBAAyB,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,wBAAwB,CAAC;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAAC;IAAEC,WAAW;IAAEC,OAAO;IAAEC,WAAW;IAAEC;EAAO,CAAC,EAAE;IACzD,IAAIH,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAII,KAAK,CAAC,mDAAmD,CAAC;IACtE;IACA,IAAI,CAACF,WAAW,GAAGA,WAAW;IAC9B,IAAI,CAACD,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACI,MAAM,GAAG,IAAIT,qBAAqB,CAAC;MACtCI,WAAW;MACXC,OAAO;MACPC,WAAW;MACXC;IACF,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEG,gBAAgBA,CAAC;IACfC,MAAM;IACNC,KAAK;IACLC,WAAW;IACXC,KAAK,GAAG,EAAE;IACVC,UAAU,GAAG,EAAE;IACfC;EACF,CAAC,EAAE;IACD,IAAI,CAACP,MAAM,CAACQ,MAAM,CAACN,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAEC,KAAK,EAAEC,UAAU,EAAEC,QAAQ,CAAC;EAC7E;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEE,0BAA0B,GAAGA,CAACC,GAAG,EAAEC,GAAG,EAAEC,IAAI,KAAK;IAC/C,IAAIF,GAAG,CAACR,MAAM,KAAK,SAAS,EAAE;MAC5BU,IAAI,CAAC,CAAC;MACN;IACF;IAEA,MAAMC,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC;IACxBJ,GAAG,CAACK,EAAE,CAAC,QAAQ,EAAE,MAAM;MACrB,MAAMb,KAAK,GAAGO,GAAG,CAACP,KAAK,EAAEc,IAAI,IAAIP,GAAG,CAACO,IAAI,IAAI,SAAS;MACtD,MAAMZ,KAAK,GACTK,GAAG,CAACQ,MAAM,EAAEb,KAAK,IAAIK,GAAG,CAACS,IAAI,EAAEd,KAAK,IAAIK,GAAG,CAACU,KAAK,EAAEf,KAAK,IAAI,EAAE;MAChE,MAAMC,UAAU,GACdI,GAAG,CAACQ,MAAM,EAAEZ,UAAU,IACtBI,GAAG,CAACS,IAAI,EAAEb,UAAU,IACpBI,GAAG,CAACU,KAAK,EAAEd,UAAU,IACrBI,GAAG,CAACQ,MAAM,EAAEG,YAAY,IACxBX,GAAG,CAACS,IAAI,EAAEE,YAAY,IACtBX,GAAG,CAACU,KAAK,EAAEC,YAAY,IACvB,EAAE;MAEJ,IAAI,CAACpB,gBAAgB,CAAC;QACpBC,MAAM,EAAEQ,GAAG,CAACR,MAAM;QAClBC,KAAK;QACLC,WAAW,EAAEO,GAAG,CAACW,UAAU;QAC3BjB,KAAK;QACLC,UAAU;QACVC,QAAQ,EAAEO,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGF;MACzB,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFD,IAAI,CAAC,CAAC;EACR,CAAC;AACH;AAEAW,MAAM,CAACC,OAAO,GAAG;EAAE/B;AAAyB,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisStore.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisStore.js"],"names":[],"mappings":"AAwDA;;;GAGG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAe9B;IATC,qCAA0B;IAC1B,eAGwC;IAExC,iBAAiD;IACjD,eAA6C;IAC7C,gBAAqD;IAGvD;;;OAGG;IACH,sBAEC;IAED;;;;;;;OAOG;IACH,eAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,cACN,MAAM,QAqBhB;IAED;;;;OAIG;IACH,qCAJoB,MAAM,SAAS,MAAM,KAAK,IAAI,0BAC9B,MAAM,SAAS,MAAM,KAAK,IAAI,GACrC,QAAQ,OAAO,CAAC,CAsE5B;CACF;AAjKD;;;;;;;GAOG;AACH,sCAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,GACJ,MAAM,CAIlB;AA3CD;;;GAGG;AACH,wBAFU,MAAM,CAEQ;AAiBxB;;GAEG;AACH,wCAFa,OAAO,CASnB;AAzBD;;;GAGG;AACH,iDAFU,MAAM,CAE8B"}
1
+ {"version":3,"file":"httpMetricsRedisStore.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisStore.js"],"names":[],"mappings":"AAkEA;;;GAGG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAiB9B;IAXC,qCAA0B;IAC1B,eAGwC;IAIxC,iBAAiD;IACjD,eAA6C;IAC7C,gBAAqD;IAGvD;;;OAGG;IACH,sBAEC;IAED;;;;;;;OAOG;IACH,eAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,cACN,MAAM,QA4BhB;IAED;;;;OAIG;IACH,qCAJoB,MAAM,SAAS,MAAM,KAAK,IAAI,0BAC9B,MAAM,SAAS,MAAM,KAAK,IAAI,GACrC,QAAQ,OAAO,CAAC,CAwH5B;CACF;AAtOD;;;;;;;GAOG;AACH,sCAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,GACJ,MAAM,CAIlB;AA3CD;;;GAGG;AACH,wBAFU,MAAM,CAEQ;AAiBxB;;GAEG;AACH,wCAFa,OAAO,CASnB;AAzBD;;;GAGG;AACH,iDAFU,MAAM,CAE8B"}
@@ -54,6 +54,15 @@ function hgetallPairsToObject(pairs) {
54
54
  return o;
55
55
  }
56
56
 
57
+ /** --- TEMP_HTTP_METRICS_DIAG: delete this helper and all calls when done debugging --- */
58
+ function tempHttpDiagLog(message) {
59
+ console.warn(`[TEMP_HTTP_METRICS_DIAG] ${message}`);
60
+ }
61
+ function truncateForDiag(s, maxLen = 220) {
62
+ const t = String(s);
63
+ return t.length > maxLen ? `${t.slice(0, maxLen - 3)}...` : t;
64
+ }
65
+
57
66
  /**
58
67
  * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).
59
68
  * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`, `set`, `del`.
@@ -104,6 +113,7 @@ class HttpMetricsRedisStore {
104
113
  const client = this._ensureClient();
105
114
  const field = buildFieldKey(method, route, statusCode, appId, databaseId);
106
115
  const dur = Math.max(0, Math.round(Number(durationMs) || 0));
116
+ tempHttpDiagLog(`http_redis_write_prepare pid=${process.pid} countKey=${this.countKey} durKey=${this.durKey} hincr_field=${truncateForDiag(field)} durationMs=${dur}`);
107
117
  client.multi().hincrby(this.countKey, field, 1).hincrby(this.durKey, field, dur).expire(this.countKey, this.ttlSec).expire(this.durKey, this.ttlSec).exec(err => {
108
118
  if (err) {
109
119
  console.error('[HttpMetricsRedisStore] record failed:', err.message);
@@ -130,6 +140,7 @@ class HttpMetricsRedisStore {
130
140
  return new Promise(resolve => {
131
141
  client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {
132
142
  if (setErr || ok !== 'OK') {
143
+ tempHttpDiagLog(`http_redis_drain_skip pid=${process.pid} countKey=${this.countKey} reason=${setErr ? setErr.message : 'lock_not_held'}`);
133
144
  resolve(false);
134
145
  return;
135
146
  }
@@ -139,17 +150,23 @@ class HttpMetricsRedisStore {
139
150
  };
140
151
  if (evalErr) {
141
152
  console.error('[HttpMetricsRedisStore] drain failed:', evalErr.message);
153
+ tempHttpDiagLog(`http_redis_drain_got_error pid=${process.pid} message=${evalErr.message}`);
142
154
  finish();
143
155
  return;
144
156
  }
145
157
  try {
146
158
  if (!raw || !Array.isArray(raw) || raw.length < 2) {
159
+ tempHttpDiagLog(`http_redis_drain_collected pid=${process.pid} countKey=${this.countKey} hash_fields=0 sum_request_counts=0 (empty — keys deleted or no data)`);
147
160
  finish();
148
161
  return;
149
162
  }
150
163
  const counts = hgetallPairsToObject(raw[0]);
151
164
  const durs = hgetallPairsToObject(raw[1]);
152
- for (const field of Object.keys(counts)) {
165
+ const fieldKeys = Object.keys(counts);
166
+ let totalUnits = 0;
167
+ let appliedGroups = 0;
168
+ const samples = [];
169
+ for (const field of fieldKeys) {
153
170
  const count = parseInt(counts[field], 10);
154
171
  if (!count || count < 1) {
155
172
  continue;
@@ -159,7 +176,12 @@ class HttpMetricsRedisStore {
159
176
  if (parts.length !== 5) {
160
177
  continue;
161
178
  }
179
+ totalUnits += count;
180
+ appliedGroups += 1;
162
181
  const [m, route, statusStr, aid, did] = parts;
182
+ if (samples.length < 4) {
183
+ samples.push(`${m} ${route} ${statusStr} x${count}`);
184
+ }
163
185
  const labels = {
164
186
  method: m,
165
187
  route,
@@ -172,8 +194,12 @@ class HttpMetricsRedisStore {
172
194
  applyDuration(labels, dur);
173
195
  }
174
196
  }
197
+ const emptyHint = fieldKeys.length === 0 ? 'no_hash_entries' : totalUnits === 0 ? 'entries_present_but_zero_counts' : 'ok';
198
+ const noTrafficHint = fieldKeys.length === 0 ? ' | if web had traffic: check web uses HttpMetricsRedisRecorder (METRICS_HTTP_ENABLED, not METRICS_HTTP_USE_BUILTIN_COUNTERS), same REDIS, BUILD_APP_NAME, key segment `web`' : '';
199
+ tempHttpDiagLog(`http_redis_drain_collected pid=${process.pid} countKey=${this.countKey} durKey=${this.durKey} hash_fields_read=${fieldKeys.length} label_groups_applied=${appliedGroups} sum_request_counts=${totalUnits} state=${emptyHint} sample=${samples.join(' | ') || '—'}${noTrafficHint}`);
175
200
  } catch (e) {
176
201
  console.error('[HttpMetricsRedisStore] flush apply failed:', e.message);
202
+ tempHttpDiagLog(`http_redis_drain_apply_failed pid=${process.pid} ${e.message}`);
177
203
  }
178
204
  finish();
179
205
  });
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisStore.js","names":["FIELD_SEP","DEFAULT_HTTP_METRICS_REDIS_TTL_SEC","DRAIN_LUA","isRedisPeerInstalled","require","resolve","buildFieldKey","method","route","statusCode","appId","databaseId","String","join","hgetallPairsToObject","pairs","o","length","i","HttpMetricsRedisStore","constructor","redisClient","appName","processType","ttlSec","Error","_client","keySeg","encodeURIComponent","countKey","durKey","lockKey","_ensureClient","record","durationMs","client","field","dur","Math","max","round","Number","multi","hincrby","expire","exec","err","console","error","message","e","flushToCounters","applyCount","applyDuration","Promise","set","setErr","ok","eval","evalErr","raw","finish","del","Array","isArray","counts","durs","Object","keys","count","parseInt","parts","split","m","statusStr","aid","did","labels","status_code","module","exports"],"sources":["../../src/metrics/httpMetricsRedisStore.js"],"sourcesContent":["/**\n * Record separator for hash fields (avoids collisions when route contains \"|\").\n * @type {string}\n */\nconst FIELD_SEP = '\\x1e'\n\n/**\n * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).\n * @type {number}\n */\nconst DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120\n\nconst DRAIN_LUA = `\nlocal function drain(key)\n local v = redis.call('HGETALL', key)\n redis.call('DEL', key)\n return v\nend\nreturn {drain(KEYS[1]), drain(KEYS[2])}\n`\n\n/**\n * @returns {boolean} Whether the `redis` npm package is resolvable (optional peer).\n */\nfunction isRedisPeerInstalled() {\n try {\n require.resolve('redis')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @returns {string}\n */\nfunction buildFieldKey(method, route, statusCode, appId, databaseId) {\n return [method, route, String(statusCode), appId, databaseId].join(FIELD_SEP)\n}\n\nfunction hgetallPairsToObject(pairs) {\n const o = {}\n if (!pairs || !pairs.length) {\n return o\n }\n for (let i = 0; i < pairs.length; i += 2) {\n o[pairs[i]] = pairs[i + 1]\n }\n return o\n}\n\n/**\n * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).\n * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`, `set`, `del`.\n */\nclass HttpMetricsRedisStore {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient\n * @param {string} opts.appName BUILD_APP_NAME (key segment)\n * @param {string} opts.processType logical process for key (e.g. web)\n * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisStore: redisClient is required')\n }\n this._client = redisClient\n this.ttlSec =\n typeof ttlSec === 'number' && ttlSec > 0\n ? ttlSec\n : DEFAULT_HTTP_METRICS_REDIS_TTL_SEC\n const keySeg = `${encodeURIComponent(appName)}:${encodeURIComponent(processType)}`\n this.countKey = `metrics:http:v2:${keySeg}:count`\n this.durKey = `metrics:http:v2:${keySeg}:dur`\n this.lockKey = `metrics:http:v2:${keySeg}:drain_lock`\n }\n\n /**\n * @returns {import('redis').RedisClient}\n * @private\n */\n _ensureClient() {\n return this._client\n }\n\n /**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @param {number} durationMs\n */\n record(method, route, statusCode, appId, databaseId, durationMs) {\n try {\n const client = this._ensureClient()\n const field = buildFieldKey(method, route, statusCode, appId, databaseId)\n const dur = Math.max(0, Math.round(Number(durationMs) || 0))\n client\n .multi()\n .hincrby(this.countKey, field, 1)\n .hincrby(this.durKey, field, dur)\n .expire(this.countKey, this.ttlSec)\n .expire(this.durKey, this.ttlSec)\n .exec(err => {\n if (err) {\n console.error('[HttpMetricsRedisStore] record failed:', err.message)\n }\n })\n } catch (e) {\n console.error('[HttpMetricsRedisStore] record:', e.message)\n }\n }\n\n /**\n * @param {(labels: Object, value: number) => void} applyCount\n * @param {(labels: Object, value: number) => void} applyDuration\n * @returns {Promise<boolean>}\n */\n flushToCounters(applyCount, applyDuration) {\n let client\n try {\n client = this._ensureClient()\n } catch (e) {\n console.error('[HttpMetricsRedisStore] flush:', e.message)\n return Promise.resolve(false)\n }\n return new Promise(resolve => {\n client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {\n if (setErr || ok !== 'OK') {\n resolve(false)\n return\n }\n client.eval(\n DRAIN_LUA,\n 2,\n this.countKey,\n this.durKey,\n (evalErr, raw) => {\n const finish = () => {\n client.del(this.lockKey, () => resolve(true))\n }\n\n if (evalErr) {\n console.error('[HttpMetricsRedisStore] drain failed:', evalErr.message)\n finish()\n return\n }\n\n try {\n if (!raw || !Array.isArray(raw) || raw.length < 2) {\n finish()\n return\n }\n const counts = hgetallPairsToObject(raw[0])\n const durs = hgetallPairsToObject(raw[1])\n for (const field of Object.keys(counts)) {\n const count = parseInt(counts[field], 10)\n if (!count || count < 1) {\n continue\n }\n const dur = parseInt(durs[field] || '0', 10) || 0\n const parts = field.split(FIELD_SEP)\n if (parts.length !== 5) {\n continue\n }\n const [m, route, statusStr, aid, did] = parts\n const labels = {\n method: m,\n route,\n status_code: statusStr,\n appId: aid,\n databaseId: did,\n }\n applyCount(labels, count)\n if (dur > 0) {\n applyDuration(labels, dur)\n }\n }\n } catch (e) {\n console.error('[HttpMetricsRedisStore] flush apply failed:', e.message)\n }\n finish()\n }\n )\n })\n })\n }\n}\n\nmodule.exports = {\n HttpMetricsRedisStore,\n buildFieldKey,\n FIELD_SEP,\n isRedisPeerInstalled,\n DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,\n}\n"],"mappings":";;AAAA;AACA;AACA;AACA;AACA,MAAMA,SAAS,GAAG,MAAM;;AAExB;AACA;AACA;AACA;AACA,MAAMC,kCAAkC,GAAG,GAAG;AAE9C,MAAMC,SAAS,GAAG;AAClB;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA,SAASC,oBAAoBA,CAAA,EAAG;EAC9B,IAAI;IACFC,OAAO,CAACC,OAAO,CAAC,OAAO,CAAC;IACxB,OAAO,IAAI;EACb,CAAC,CAAC,MAAM;IACN,OAAO,KAAK;EACd;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,aAAaA,CAACC,MAAM,EAAEC,KAAK,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,EAAE;EACnE,OAAO,CAACJ,MAAM,EAAEC,KAAK,EAAEI,MAAM,CAACH,UAAU,CAAC,EAAEC,KAAK,EAAEC,UAAU,CAAC,CAACE,IAAI,CAACb,SAAS,CAAC;AAC/E;AAEA,SAASc,oBAAoBA,CAACC,KAAK,EAAE;EACnC,MAAMC,CAAC,GAAG,CAAC,CAAC;EACZ,IAAI,CAACD,KAAK,IAAI,CAACA,KAAK,CAACE,MAAM,EAAE;IAC3B,OAAOD,CAAC;EACV;EACA,KAAK,IAAIE,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGH,KAAK,CAACE,MAAM,EAAEC,CAAC,IAAI,CAAC,EAAE;IACxCF,CAAC,CAACD,KAAK,CAACG,CAAC,CAAC,CAAC,GAAGH,KAAK,CAACG,CAAC,GAAG,CAAC,CAAC;EAC5B;EACA,OAAOF,CAAC;AACV;;AAEA;AACA;AACA;AACA;AACA,MAAMG,qBAAqB,CAAC;EAC1B;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAAC;IAAEC,WAAW;IAAEC,OAAO;IAAEC,WAAW;IAAEC;EAAO,CAAC,EAAE;IACzD,IAAIH,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAII,KAAK,CAAC,gDAAgD,CAAC;IACnE;IACA,IAAI,CAACC,OAAO,GAAGL,WAAW;IAC1B,IAAI,CAACG,MAAM,GACT,OAAOA,MAAM,KAAK,QAAQ,IAAIA,MAAM,GAAG,CAAC,GACpCA,MAAM,GACNvB,kCAAkC;IACxC,MAAM0B,MAAM,GAAG,GAAGC,kBAAkB,CAACN,OAAO,CAAC,IAAIM,kBAAkB,CAACL,WAAW,CAAC,EAAE;IAClF,IAAI,CAACM,QAAQ,GAAG,mBAAmBF,MAAM,QAAQ;IACjD,IAAI,CAACG,MAAM,GAAG,mBAAmBH,MAAM,MAAM;IAC7C,IAAI,CAACI,OAAO,GAAG,mBAAmBJ,MAAM,aAAa;EACvD;;EAEA;AACF;AACA;AACA;EACEK,aAAaA,CAAA,EAAG;IACd,OAAO,IAAI,CAACN,OAAO;EACrB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,MAAMA,CAAC1B,MAAM,EAAEC,KAAK,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,EAAEuB,UAAU,EAAE;IAC/D,IAAI;MACF,MAAMC,MAAM,GAAG,IAAI,CAACH,aAAa,CAAC,CAAC;MACnC,MAAMI,KAAK,GAAG9B,aAAa,CAACC,MAAM,EAAEC,KAAK,EAAEC,UAAU,EAAEC,KAAK,EAAEC,UAAU,CAAC;MACzE,MAAM0B,GAAG,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,KAAK,CAACC,MAAM,CAACP,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;MAC5DC,MAAM,CACHO,KAAK,CAAC,CAAC,CACPC,OAAO,CAAC,IAAI,CAACd,QAAQ,EAAEO,KAAK,EAAE,CAAC,CAAC,CAChCO,OAAO,CAAC,IAAI,CAACb,MAAM,EAAEM,KAAK,EAAEC,GAAG,CAAC,CAChCO,MAAM,CAAC,IAAI,CAACf,QAAQ,EAAE,IAAI,CAACL,MAAM,CAAC,CAClCoB,MAAM,CAAC,IAAI,CAACd,MAAM,EAAE,IAAI,CAACN,MAAM,CAAC,CAChCqB,IAAI,CAACC,GAAG,IAAI;QACX,IAAIA,GAAG,EAAE;UACPC,OAAO,CAACC,KAAK,CAAC,wCAAwC,EAAEF,GAAG,CAACG,OAAO,CAAC;QACtE;MACF,CAAC,CAAC;IACN,CAAC,CAAC,OAAOC,CAAC,EAAE;MACVH,OAAO,CAACC,KAAK,CAAC,iCAAiC,EAAEE,CAAC,CAACD,OAAO,CAAC;IAC7D;EACF;;EAEA;AACF;AACA;AACA;AACA;EACEE,eAAeA,CAACC,UAAU,EAAEC,aAAa,EAAE;IACzC,IAAIlB,MAAM;IACV,IAAI;MACFA,MAAM,GAAG,IAAI,CAACH,aAAa,CAAC,CAAC;IAC/B,CAAC,CAAC,OAAOkB,CAAC,EAAE;MACVH,OAAO,CAACC,KAAK,CAAC,gCAAgC,EAAEE,CAAC,CAACD,OAAO,CAAC;MAC1D,OAAOK,OAAO,CAACjD,OAAO,CAAC,KAAK,CAAC;IAC/B;IACA,OAAO,IAAIiD,OAAO,CAACjD,OAAO,IAAI;MAC5B8B,MAAM,CAACoB,GAAG,CAAC,IAAI,CAACxB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAACyB,MAAM,EAAEC,EAAE,KAAK;QAC5D,IAAID,MAAM,IAAIC,EAAE,KAAK,IAAI,EAAE;UACzBpD,OAAO,CAAC,KAAK,CAAC;UACd;QACF;QACA8B,MAAM,CAACuB,IAAI,CACTxD,SAAS,EACT,CAAC,EACD,IAAI,CAAC2B,QAAQ,EACb,IAAI,CAACC,MAAM,EACX,CAAC6B,OAAO,EAAEC,GAAG,KAAK;UAChB,MAAMC,MAAM,GAAGA,CAAA,KAAM;YACnB1B,MAAM,CAAC2B,GAAG,CAAC,IAAI,CAAC/B,OAAO,EAAE,MAAM1B,OAAO,CAAC,IAAI,CAAC,CAAC;UAC/C,CAAC;UAED,IAAIsD,OAAO,EAAE;YACXZ,OAAO,CAACC,KAAK,CAAC,uCAAuC,EAAEW,OAAO,CAACV,OAAO,CAAC;YACvEY,MAAM,CAAC,CAAC;YACR;UACF;UAEA,IAAI;YACF,IAAI,CAACD,GAAG,IAAI,CAACG,KAAK,CAACC,OAAO,CAACJ,GAAG,CAAC,IAAIA,GAAG,CAAC3C,MAAM,GAAG,CAAC,EAAE;cACjD4C,MAAM,CAAC,CAAC;cACR;YACF;YACA,MAAMI,MAAM,GAAGnD,oBAAoB,CAAC8C,GAAG,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAMM,IAAI,GAAGpD,oBAAoB,CAAC8C,GAAG,CAAC,CAAC,CAAC,CAAC;YACzC,KAAK,MAAMxB,KAAK,IAAI+B,MAAM,CAACC,IAAI,CAACH,MAAM,CAAC,EAAE;cACvC,MAAMI,KAAK,GAAGC,QAAQ,CAACL,MAAM,CAAC7B,KAAK,CAAC,EAAE,EAAE,CAAC;cACzC,IAAI,CAACiC,KAAK,IAAIA,KAAK,GAAG,CAAC,EAAE;gBACvB;cACF;cACA,MAAMhC,GAAG,GAAGiC,QAAQ,CAACJ,IAAI,CAAC9B,KAAK,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC;cACjD,MAAMmC,KAAK,GAAGnC,KAAK,CAACoC,KAAK,CAACxE,SAAS,CAAC;cACpC,IAAIuE,KAAK,CAACtD,MAAM,KAAK,CAAC,EAAE;gBACtB;cACF;cACA,MAAM,CAACwD,CAAC,EAAEjE,KAAK,EAAEkE,SAAS,EAAEC,GAAG,EAAEC,GAAG,CAAC,GAAGL,KAAK;cAC7C,MAAMM,MAAM,GAAG;gBACbtE,MAAM,EAAEkE,CAAC;gBACTjE,KAAK;gBACLsE,WAAW,EAAEJ,SAAS;gBACtBhE,KAAK,EAAEiE,GAAG;gBACVhE,UAAU,EAAEiE;cACd,CAAC;cACDxB,UAAU,CAACyB,MAAM,EAAER,KAAK,CAAC;cACzB,IAAIhC,GAAG,GAAG,CAAC,EAAE;gBACXgB,aAAa,CAACwB,MAAM,EAAExC,GAAG,CAAC;cAC5B;YACF;UACF,CAAC,CAAC,OAAOa,CAAC,EAAE;YACVH,OAAO,CAACC,KAAK,CAAC,6CAA6C,EAAEE,CAAC,CAACD,OAAO,CAAC;UACzE;UACAY,MAAM,CAAC,CAAC;QACV,CACF,CAAC;MACH,CAAC,CAAC;IACJ,CAAC,CAAC;EACJ;AACF;AAEAkB,MAAM,CAACC,OAAO,GAAG;EACf7D,qBAAqB;EACrBb,aAAa;EACbN,SAAS;EACTG,oBAAoB;EACpBF;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisStore.js","names":["FIELD_SEP","DEFAULT_HTTP_METRICS_REDIS_TTL_SEC","DRAIN_LUA","isRedisPeerInstalled","require","resolve","buildFieldKey","method","route","statusCode","appId","databaseId","String","join","hgetallPairsToObject","pairs","o","length","i","tempHttpDiagLog","message","console","warn","truncateForDiag","s","maxLen","t","slice","HttpMetricsRedisStore","constructor","redisClient","appName","processType","ttlSec","Error","_client","keySeg","encodeURIComponent","countKey","durKey","lockKey","_ensureClient","record","durationMs","client","field","dur","Math","max","round","Number","process","pid","multi","hincrby","expire","exec","err","error","e","flushToCounters","applyCount","applyDuration","Promise","set","setErr","ok","eval","evalErr","raw","finish","del","Array","isArray","counts","durs","fieldKeys","Object","keys","totalUnits","appliedGroups","samples","count","parseInt","parts","split","m","statusStr","aid","did","push","labels","status_code","emptyHint","noTrafficHint","module","exports"],"sources":["../../src/metrics/httpMetricsRedisStore.js"],"sourcesContent":["/**\n * Record separator for hash fields (avoids collisions when route contains \"|\").\n * @type {string}\n */\nconst FIELD_SEP = '\\x1e'\n\n/**\n * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).\n * @type {number}\n */\nconst DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120\n\nconst DRAIN_LUA = `\nlocal function drain(key)\n local v = redis.call('HGETALL', key)\n redis.call('DEL', key)\n return v\nend\nreturn {drain(KEYS[1]), drain(KEYS[2])}\n`\n\n/**\n * @returns {boolean} Whether the `redis` npm package is resolvable (optional peer).\n */\nfunction isRedisPeerInstalled() {\n try {\n require.resolve('redis')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @returns {string}\n */\nfunction buildFieldKey(method, route, statusCode, appId, databaseId) {\n return [method, route, String(statusCode), appId, databaseId].join(FIELD_SEP)\n}\n\nfunction hgetallPairsToObject(pairs) {\n const o = {}\n if (!pairs || !pairs.length) {\n return o\n }\n for (let i = 0; i < pairs.length; i += 2) {\n o[pairs[i]] = pairs[i + 1]\n }\n return o\n}\n\n/** --- TEMP_HTTP_METRICS_DIAG: delete this helper and all calls when done debugging --- */\nfunction tempHttpDiagLog(message) {\n console.warn(`[TEMP_HTTP_METRICS_DIAG] ${message}`)\n}\n\nfunction truncateForDiag(s, maxLen = 220) {\n const t = String(s)\n return t.length > maxLen ? `${t.slice(0, maxLen - 3)}...` : t\n}\n\n/**\n * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).\n * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`, `set`, `del`.\n */\nclass HttpMetricsRedisStore {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient\n * @param {string} opts.appName BUILD_APP_NAME (key segment)\n * @param {string} opts.processType logical process for key (e.g. web)\n * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisStore: redisClient is required')\n }\n this._client = redisClient\n this.ttlSec =\n typeof ttlSec === 'number' && ttlSec > 0\n ? ttlSec\n : DEFAULT_HTTP_METRICS_REDIS_TTL_SEC\n const keySeg = `${encodeURIComponent(appName)}:${encodeURIComponent(\n processType\n )}`\n this.countKey = `metrics:http:v2:${keySeg}:count`\n this.durKey = `metrics:http:v2:${keySeg}:dur`\n this.lockKey = `metrics:http:v2:${keySeg}:drain_lock`\n }\n\n /**\n * @returns {import('redis').RedisClient}\n * @private\n */\n _ensureClient() {\n return this._client\n }\n\n /**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @param {number} durationMs\n */\n record(method, route, statusCode, appId, databaseId, durationMs) {\n try {\n const client = this._ensureClient()\n const field = buildFieldKey(method, route, statusCode, appId, databaseId)\n const dur = Math.max(0, Math.round(Number(durationMs) || 0))\n tempHttpDiagLog(\n `http_redis_write_prepare pid=${process.pid} countKey=${\n this.countKey\n } durKey=${this.durKey} hincr_field=${truncateForDiag(\n field\n )} durationMs=${dur}`\n )\n client\n .multi()\n .hincrby(this.countKey, field, 1)\n .hincrby(this.durKey, field, dur)\n .expire(this.countKey, this.ttlSec)\n .expire(this.durKey, this.ttlSec)\n .exec(err => {\n if (err) {\n console.error('[HttpMetricsRedisStore] record failed:', err.message)\n }\n })\n } catch (e) {\n console.error('[HttpMetricsRedisStore] record:', e.message)\n }\n }\n\n /**\n * @param {(labels: Object, value: number) => void} applyCount\n * @param {(labels: Object, value: number) => void} applyDuration\n * @returns {Promise<boolean>}\n */\n flushToCounters(applyCount, applyDuration) {\n let client\n try {\n client = this._ensureClient()\n } catch (e) {\n console.error('[HttpMetricsRedisStore] flush:', e.message)\n return Promise.resolve(false)\n }\n return new Promise(resolve => {\n client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {\n if (setErr || ok !== 'OK') {\n tempHttpDiagLog(\n `http_redis_drain_skip pid=${process.pid} countKey=${\n this.countKey\n } reason=${setErr ? setErr.message : 'lock_not_held'}`\n )\n resolve(false)\n return\n }\n client.eval(\n DRAIN_LUA,\n 2,\n this.countKey,\n this.durKey,\n (evalErr, raw) => {\n const finish = () => {\n client.del(this.lockKey, () => resolve(true))\n }\n\n if (evalErr) {\n console.error(\n '[HttpMetricsRedisStore] drain failed:',\n evalErr.message\n )\n tempHttpDiagLog(\n `http_redis_drain_got_error pid=${process.pid} message=${evalErr.message}`\n )\n finish()\n return\n }\n\n try {\n if (!raw || !Array.isArray(raw) || raw.length < 2) {\n tempHttpDiagLog(\n `http_redis_drain_collected pid=${process.pid} countKey=${\n this.countKey\n } hash_fields=0 sum_request_counts=0 (empty — keys deleted or no data)`\n )\n finish()\n return\n }\n const counts = hgetallPairsToObject(raw[0])\n const durs = hgetallPairsToObject(raw[1])\n const fieldKeys = Object.keys(counts)\n let totalUnits = 0\n let appliedGroups = 0\n const samples = []\n for (const field of fieldKeys) {\n const count = parseInt(counts[field], 10)\n if (!count || count < 1) {\n continue\n }\n const dur = parseInt(durs[field] || '0', 10) || 0\n const parts = field.split(FIELD_SEP)\n if (parts.length !== 5) {\n continue\n }\n totalUnits += count\n appliedGroups += 1\n const [m, route, statusStr, aid, did] = parts\n if (samples.length < 4) {\n samples.push(`${m} ${route} ${statusStr} x${count}`)\n }\n const labels = {\n method: m,\n route,\n status_code: statusStr,\n appId: aid,\n databaseId: did,\n }\n applyCount(labels, count)\n if (dur > 0) {\n applyDuration(labels, dur)\n }\n }\n const emptyHint =\n fieldKeys.length === 0\n ? 'no_hash_entries'\n : totalUnits === 0\n ? 'entries_present_but_zero_counts'\n : 'ok'\n const noTrafficHint =\n fieldKeys.length === 0\n ? ' | if web had traffic: check web uses HttpMetricsRedisRecorder (METRICS_HTTP_ENABLED, not METRICS_HTTP_USE_BUILTIN_COUNTERS), same REDIS, BUILD_APP_NAME, key segment `web`'\n : ''\n tempHttpDiagLog(\n `http_redis_drain_collected pid=${process.pid} countKey=${\n this.countKey\n } durKey=${this.durKey} hash_fields_read=${\n fieldKeys.length\n } label_groups_applied=${appliedGroups} sum_request_counts=${totalUnits} state=${emptyHint} sample=${samples.join(\n ' | '\n ) || '—'}${noTrafficHint}`\n )\n } catch (e) {\n console.error(\n '[HttpMetricsRedisStore] flush apply failed:',\n e.message\n )\n tempHttpDiagLog(\n `http_redis_drain_apply_failed pid=${process.pid} ${e.message}`\n )\n }\n finish()\n }\n )\n })\n })\n }\n}\n\nmodule.exports = {\n HttpMetricsRedisStore,\n buildFieldKey,\n FIELD_SEP,\n isRedisPeerInstalled,\n DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsDatabaseClient.js"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;QAf2B,WAAW,EAA3B,MAAM;QACU,YAAY,EAA5B,MAAM;QAC2B,wBAAwB;;;QACxC,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAoF9C;IAtBC,qBAAuB;IAUvB,mDAAmD;IACnD,8DAQE;IAKJ,yCAKC;IAED;;;OAGG;IACH,gCAHW,IAAI,KACF,QAAQ;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CA4BrE;IAED;;;OAGG;IACH,8BAFa,QAAQ,IAAI,CAAC,CAsBzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAqBzB;IAED;;;OAGG;IACH,sDAMC;CAwBF"}
1
+ {"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsDatabaseClient.js"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;QAf2B,WAAW,EAA3B,MAAM;QACU,YAAY,EAA5B,MAAM;QAC2B,wBAAwB;;;QACxC,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAoF9C;IAtBC,qBAAuB;IAUvB,mDAAmD;IACnD,8DAQE;IAKJ,yCAKC;IAED;;;OAGG;IACH,gCAHW,IAAI,KACF,QAAQ;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CA4BrE;IAED;;;OAGG;IACH,8BAFa,QAAQ,IAAI,CAAC,CAsBzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAqBzB;IAED;;;OAGG;IACH,sDAMC;CAwBF"}
@@ -7,7 +7,8 @@ const {
7
7
  BaseMetricsClient
8
8
  } = require('./baseMetricsClient');
9
9
  const {
10
- exitUnlessMetricsProcessType
10
+ exitUnlessProcessTypeIs,
11
+ METRICS_PROCESS_TYPE_DATABASE
11
12
  } = require('./metricsProcessTypeUtils');
12
13
 
13
14
  /**
@@ -40,7 +41,7 @@ class DatabaseMetricsClient extends BaseMetricsClient {
40
41
  additional_database_urls = {},
41
42
  ...metricsConfig
42
43
  } = {}) {
43
- exitUnlessMetricsProcessType(metricsConfig, 'database-metrics', 'database-metrics');
44
+ exitUnlessProcessTypeIs(metricsConfig, METRICS_PROCESS_TYPE_DATABASE);
44
45
  const intervalSec = metricsConfig.intervalSec || parseInt(process.env.METRICS_DATABASE_INTERVAL_SEC || '', 10) || 60;
45
46
  const tmpAppName = metricsConfig.appName || process.env.BUILD_APP_NAME || 'unknown-app';
46
47
  const mainDbName = databaseName || `${tmpAppName}_db`;
@@ -78,7 +79,7 @@ class DatabaseMetricsClient extends BaseMetricsClient {
78
79
  };
79
80
  super({
80
81
  ...metricsConfig,
81
- processType: metricsConfig.processType || 'database-metrics',
82
+ processType: metricsConfig.processType || METRICS_PROCESS_TYPE_DATABASE,
82
83
  intervalSec,
83
84
  startupValidation
84
85
  });
@@ -1 +1 @@
1
- {"version":3,"file":"metricsDatabaseClient.js","names":["Pool","require","BaseMetricsClient","exitUnlessMetricsProcessType","DatabaseMetricsClient","constructor","databaseUrl","databaseName","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","tmpAppName","appName","BUILD_APP_NAME","mainDbName","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","dbKey","url","Object","entries","p","processType","databasePools","_addPool","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabelsWithoutDynoId","_setCleanupHandlers","pool","push","getDBConnectionsAndName","currentRes","current","rows","maxRes","max","dbName","options","database","URL","pathname","replace","collectDatabaseMetrics","set","defaultLabelsWithoutDynoId","database_name","max_connections","String","db_key","warn","pushDatabaseMetrics","gatewayPush","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../../src/metrics/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\nconst { exitUnlessMetricsProcessType } = require('./metricsProcessTypeUtils')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends BaseMetricsClient\n */\nclass DatabaseMetricsClient extends BaseMetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string} options.databaseName - Main database name for metrics\n * @param {Object<string, string>} [options.additional_database_urls] - Optional additional DBs, keyed by custom name with URL as value\n * @param {string} [options.appName] - Application name (from BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {function} [options.startupValidation] - Function to validate startup\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor({\n databaseUrl,\n databaseName,\n additional_database_urls = {},\n ...metricsConfig\n } = {}) {\n exitUnlessMetricsProcessType(metricsConfig, 'database-metrics', 'database-metrics')\n\n const intervalSec =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_DATABASE_INTERVAL_SEC || '', 10) ||\n 60\n\n const tmpAppName =\n metricsConfig.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n const mainDbName = databaseName || `${tmpAppName}_db`\n\n const startupValidation = async () => {\n if (!databaseUrl) {\n console.error(`[database-metrics] ❌ METRICS_DATABASE_URL is required`)\n return false\n }\n\n try {\n const mainPool = new Pool({ connectionString: databaseUrl })\n await mainPool.query('SELECT 1')\n await mainPool.end()\n console.info(`[database-metrics] ✓ Main database OK: ${mainDbName}`)\n } catch (err) {\n console.error(\n `[database-metrics] ❌ Cannot connect to main database: ${err.message}`\n )\n return false\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n try {\n const p = new Pool({ connectionString: url })\n await p.query('SELECT 1')\n await p.end()\n console.info(`[database-metrics] ✓ Additional database OK: ${dbKey}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${dbKey}`\n )\n console.error(`[database-metrics] ${err.message}`)\n }\n }\n\n console.info(`[database-metrics] Database metrics collection starting`)\n return true\n }\n\n super({\n ...metricsConfig,\n processType: metricsConfig.processType || 'database-metrics',\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this._addPool(databaseUrl, mainDbName)\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n this._addPool(url, dbKey)\n }\n\n /** Gauge for Database connections (no dyno_id). */\n this.databaseConnectionsGauge = this.createGauge({\n name: 'app_database_connections',\n help: 'Postgres database connections',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'max_connections',\n 'database_name',\n 'db_key',\n ]),\n })\n\n this._setCleanupHandlers()\n }\n\n _addPool = (url, dbKey) => {\n const pool = new Pool({ connectionString: url })\n pool.dbKey = dbKey\n this.databasePools.push(pool)\n return pool\n }\n\n /**\n * @param {Pool} pool - PG connection pool\n * @returns {Promise<{ current: number, max: number, dbName: string }>}\n */\n getDBConnectionsAndName = async pool => {\n try {\n const currentRes = await pool.query(\n 'SELECT COUNT(*) AS current FROM pg_stat_activity WHERE datname = current_database()'\n )\n const current = parseInt(currentRes.rows[0]?.current || 0, 10)\n\n const maxRes = await pool.query(\n \"SELECT current_setting('max_connections') AS max\"\n )\n const max = parseInt(maxRes.rows[0]?.max || 0, 10)\n\n let dbName = pool.options?.database\n if (!dbName && pool.options?.connectionString) {\n try {\n const url = new URL(pool.options.connectionString)\n dbName = url.pathname.replace(/^\\//, '')\n } catch {\n dbName = pool.options.connectionString\n }\n }\n\n return { current, max, dbName }\n } catch (err) {\n return { current: 0, max: 0, dbName: pool.options?.database || '' }\n }\n }\n\n /**\n * Collect database connection metrics for all configured pools\n * @returns {Promise<void>}\n */\n collectDatabaseMetrics = async () => {\n for (const pool of this.databasePools) {\n try {\n const { current, max, dbName } = await this.getDBConnectionsAndName(\n pool\n )\n\n this.databaseConnectionsGauge.set(\n {\n ...this.defaultLabelsWithoutDynoId,\n database_name: pool.dbKey,\n max_connections: String(max),\n db_key: dbName,\n },\n current\n )\n } catch (err) {\n console.warn(`[database-metrics] Failed to collect: ${err.message}`)\n }\n }\n }\n\n /**\n * Push database metrics to Prometheus Pushgateway\n * @returns {Promise<void>}\n */\n pushDatabaseMetrics = async () => {\n try {\n await this.collectDatabaseMetrics()\n await this.gatewayPush()\n this.clearAllCounters()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[database-metrics] Collected DB metrics`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n console.error(\n `[database-metrics] Failed to collect DB metrics: ${error.message}`\n )\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.pushDatabaseMetrics().catch(err => {\n console.error(`[database-metrics] Failed to push DB metrics:`, err)\n })\n })\n }\n\n /**\n * Cleanup database pools and exit process\n * @returns {Promise<void>}\n */\n cleanup = async () => {\n try {\n if (this.databasePools) {\n for (const pool of this.databasePools) {\n await pool.end()\n }\n }\n } catch (err) {\n console.error('[database-metrics] Error during cleanup:', err)\n }\n\n process.exit(0)\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { DatabaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"metricsDatabaseClient.js","names":["Pool","require","BaseMetricsClient","exitUnlessProcessTypeIs","METRICS_PROCESS_TYPE_DATABASE","DatabaseMetricsClient","constructor","databaseUrl","databaseName","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","tmpAppName","appName","BUILD_APP_NAME","mainDbName","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","dbKey","url","Object","entries","p","processType","databasePools","_addPool","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabelsWithoutDynoId","_setCleanupHandlers","pool","push","getDBConnectionsAndName","currentRes","current","rows","maxRes","max","dbName","options","database","URL","pathname","replace","collectDatabaseMetrics","set","defaultLabelsWithoutDynoId","database_name","max_connections","String","db_key","warn","pushDatabaseMetrics","gatewayPush","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../../src/metrics/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\nconst {\n exitUnlessProcessTypeIs,\n METRICS_PROCESS_TYPE_DATABASE,\n} = require('./metricsProcessTypeUtils')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends BaseMetricsClient\n */\nclass DatabaseMetricsClient extends BaseMetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string} options.databaseName - Main database name for metrics\n * @param {Object<string, string>} [options.additional_database_urls] - Optional additional DBs, keyed by custom name with URL as value\n * @param {string} [options.appName] - Application name (from BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {function} [options.startupValidation] - Function to validate startup\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor({\n databaseUrl,\n databaseName,\n additional_database_urls = {},\n ...metricsConfig\n } = {}) {\n exitUnlessProcessTypeIs(metricsConfig, METRICS_PROCESS_TYPE_DATABASE)\n\n const intervalSec =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_DATABASE_INTERVAL_SEC || '', 10) ||\n 60\n\n const tmpAppName =\n metricsConfig.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n const mainDbName = databaseName || `${tmpAppName}_db`\n\n const startupValidation = async () => {\n if (!databaseUrl) {\n console.error(`[database-metrics] ❌ METRICS_DATABASE_URL is required`)\n return false\n }\n\n try {\n const mainPool = new Pool({ connectionString: databaseUrl })\n await mainPool.query('SELECT 1')\n await mainPool.end()\n console.info(`[database-metrics] ✓ Main database OK: ${mainDbName}`)\n } catch (err) {\n console.error(\n `[database-metrics] ❌ Cannot connect to main database: ${err.message}`\n )\n return false\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n try {\n const p = new Pool({ connectionString: url })\n await p.query('SELECT 1')\n await p.end()\n console.info(`[database-metrics] ✓ Additional database OK: ${dbKey}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${dbKey}`\n )\n console.error(`[database-metrics] ${err.message}`)\n }\n }\n\n console.info(`[database-metrics] Database metrics collection starting`)\n return true\n }\n\n super({\n ...metricsConfig,\n processType: metricsConfig.processType || METRICS_PROCESS_TYPE_DATABASE,\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this._addPool(databaseUrl, mainDbName)\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n this._addPool(url, dbKey)\n }\n\n /** Gauge for Database connections (no dyno_id). */\n this.databaseConnectionsGauge = this.createGauge({\n name: 'app_database_connections',\n help: 'Postgres database connections',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'max_connections',\n 'database_name',\n 'db_key',\n ]),\n })\n\n this._setCleanupHandlers()\n }\n\n _addPool = (url, dbKey) => {\n const pool = new Pool({ connectionString: url })\n pool.dbKey = dbKey\n this.databasePools.push(pool)\n return pool\n }\n\n /**\n * @param {Pool} pool - PG connection pool\n * @returns {Promise<{ current: number, max: number, dbName: string }>}\n */\n getDBConnectionsAndName = async pool => {\n try {\n const currentRes = await pool.query(\n 'SELECT COUNT(*) AS current FROM pg_stat_activity WHERE datname = current_database()'\n )\n const current = parseInt(currentRes.rows[0]?.current || 0, 10)\n\n const maxRes = await pool.query(\n \"SELECT current_setting('max_connections') AS max\"\n )\n const max = parseInt(maxRes.rows[0]?.max || 0, 10)\n\n let dbName = pool.options?.database\n if (!dbName && pool.options?.connectionString) {\n try {\n const url = new URL(pool.options.connectionString)\n dbName = url.pathname.replace(/^\\//, '')\n } catch {\n dbName = pool.options.connectionString\n }\n }\n\n return { current, max, dbName }\n } catch (err) {\n return { current: 0, max: 0, dbName: pool.options?.database || '' }\n }\n }\n\n /**\n * Collect database connection metrics for all configured pools\n * @returns {Promise<void>}\n */\n collectDatabaseMetrics = async () => {\n for (const pool of this.databasePools) {\n try {\n const { current, max, dbName } = await this.getDBConnectionsAndName(\n pool\n )\n\n this.databaseConnectionsGauge.set(\n {\n ...this.defaultLabelsWithoutDynoId,\n database_name: pool.dbKey,\n max_connections: String(max),\n db_key: dbName,\n },\n current\n )\n } catch (err) {\n console.warn(`[database-metrics] Failed to collect: ${err.message}`)\n }\n }\n }\n\n /**\n * Push database metrics to Prometheus Pushgateway\n * @returns {Promise<void>}\n */\n pushDatabaseMetrics = async () => {\n try {\n await this.collectDatabaseMetrics()\n await this.gatewayPush()\n this.clearAllCounters()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[database-metrics] Collected DB metrics`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n console.error(\n `[database-metrics] Failed to collect DB metrics: ${error.message}`\n )\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.pushDatabaseMetrics().catch(err => {\n console.error(`[database-metrics] Failed to push DB metrics:`, err)\n })\n })\n }\n\n /**\n * Cleanup database pools and exit process\n * @returns {Promise<void>}\n */\n cleanup = async () => {\n try {\n if (this.databasePools) {\n for (const pool of this.databasePools) {\n await pool.end()\n }\n }\n } catch (err) {\n console.error('[database-metrics] Error during cleanup:', err)\n }\n\n process.exit(0)\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { DatabaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
@@ -1,29 +1,60 @@
1
- /**
2
- * Helpers for resolving `processType` and silently exiting when a specialized metrics client
3
- * is constructed on the wrong dyno / process (no log output).
4
- *
5
- * @module metrics/metricsProcessTypeUtils
6
- */
7
1
  /**
8
2
  * Resolve logical process type the same way specialized metrics clients do.
9
3
  *
10
4
  * @param {{ processType?: string }} metricsConfig - Remainder of constructor options (e.g. after destructuring `redisClient`, `databaseUrl`, …)
11
- * @param {string} defaultProcessType - Fallback when config and env are unset
5
+ * @param {string} defaultProcessType - Fallback when `metricsConfig.processType` and `BUILD_DYNO_PROCESS_TYPE` are unset
12
6
  * @returns {string}
13
7
  */
14
8
  export function resolveMetricsProcessType(metricsConfig: {
15
9
  processType?: string;
16
10
  }, defaultProcessType: string): string;
17
11
  /**
18
- * If the resolved process type is not in the allowlist, exit immediately with no logs (`process.exit(0)`).
19
- * Used so the wrong dyno type does not run a specialized metrics client.
12
+ * Exit with no logs if the resolved process type is not exactly `expectedProcessType`.
20
13
  *
21
14
  * @param {{ processType?: string }} metricsConfig
22
- * @param {string} defaultProcessType - Fallback for resolution only (when `processType` and env are unset)
23
- * @param {string | string[]} allowed - Single type or allowlist (e.g. `['redis-metrics','queue-metrics']`)
15
+ * @param {string} expectedProcessType - Single allowed value (use a module constant, e.g. {@link METRICS_PROCESS_TYPE_DATABASE})
24
16
  * @returns {void}
25
17
  */
26
- export function exitUnlessMetricsProcessType(metricsConfig: {
18
+ export function exitUnlessProcessTypeIs(metricsConfig: {
27
19
  processType?: string;
28
- }, defaultProcessType: string, allowed: string | string[]): void;
20
+ }, expectedProcessType: string): void;
21
+ /**
22
+ * Exit with no logs if the resolved process type is not in `allowedProcessTypes`.
23
+ *
24
+ * @param {{ processType?: string }} metricsConfig
25
+ * @param {readonly string[]} allowedProcessTypes
26
+ * @param {string} defaultWhenUnspecified - Used only to resolve when config/env omit `processType` (e.g. {@link METRICS_PROCESS_TYPE_QUEUE} for {@link RedisMetricsClient})
27
+ * @returns {void}
28
+ */
29
+ export function exitUnlessProcessTypeIn(metricsConfig: {
30
+ processType?: string;
31
+ }, allowedProcessTypes: readonly string[], defaultWhenUnspecified: string): void;
32
+ /**
33
+ * Helpers for resolving `processType` and silently exiting when a specialized metrics client
34
+ * is constructed on the wrong dyno / process (no log output).
35
+ *
36
+ * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,
37
+ * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds
38
+ * — it does not run HTTP request metrics; HTTP Redis **writers** use `web` (or `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`);
39
+ * the HTTP **collector** dyno uses {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
40
+ *
41
+ * @module metrics/metricsProcessTypeUtils
42
+ */
43
+ /** DB-only metrics dyno (`database-metrics` in Procfile). */
44
+ export const METRICS_PROCESS_TYPE_DATABASE: "database-metrics";
45
+ /** Queue + Redis metrics dyno (`queue-metrics` in Procfile). */
46
+ export const METRICS_PROCESS_TYPE_QUEUE: "queue-metrics";
47
+ /** Redis-only metrics dyno (no Bee Queue; optional separate process). */
48
+ export const METRICS_PROCESS_TYPE_REDIS: "redis-metrics";
49
+ /** Web servers — HTTP traffic, HTTP Redis **writers** typically use this in Redis key segment. */
50
+ export const METRICS_PROCESS_TYPE_WEB: "web";
51
+ /** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */
52
+ export const METRICS_PROCESS_TYPE_WORKER: "worker";
53
+ /** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */
54
+ export const METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR: "http-metrics";
55
+ /**
56
+ * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).
57
+ * @type {readonly string[]}
58
+ */
59
+ export const REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES: readonly string[];
29
60
  //# sourceMappingURL=metricsProcessTypeUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metricsProcessTypeUtils.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsProcessTypeUtils.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,yDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBACxB,MAAM,GACJ,MAAM,CAQlB;AAED;;;;;;;;GAQG;AACH,4DALW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBACxB,MAAM,WACN,MAAM,GAAG,MAAM,EAAE,GACf,IAAI,CAQhB"}
1
+ {"version":3,"file":"metricsProcessTypeUtils.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsProcessTypeUtils.js"],"names":[],"mappings":"AAuCA;;;;;;GAMG;AACH,yDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBACxB,MAAM,GACJ,MAAM,CAQlB;AAED;;;;;;GAMG;AACH,uDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,MAAM,GACJ,IAAI,CAUhB;AAED;;;;;;;GAOG;AACH,uDALW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,SAAS,MAAM,EAAE,0BACjB,MAAM,GACJ,IAAI,CAchB;AA3FD;;;;;;;;;;GAUG;AAEH,6DAA6D;AAC7D,+DAAwD;AAExD,gEAAgE;AAChE,yDAAkD;AAElD,yEAAyE;AACzE,yDAAkD;AAElD,kGAAkG;AAClG,6CAAsC;AAEtC,gGAAgG;AAChG,mDAA4C;AAE5C,wGAAwG;AACxG,yEAAkE;AAElE;;;GAGG;AACH,yDAFU,SAAS,MAAM,EAAE,CAKzB"}