@worker-manager/metrics 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -6
- package/dist/HistoryAdmin.d.ts +23 -87
- package/dist/HistoryAdmin.js +21 -330
- package/dist/HistoryAdmin.js.map +1 -1
- package/dist/HistoryStore.d.ts +9 -9
- package/dist/HistoryStore.js +22 -0
- package/dist/HistoryStore.js.map +1 -1
- package/dist/LatencySampler.d.ts +27 -32
- package/dist/LatencySampler.js +55 -156
- package/dist/LatencySampler.js.map +1 -1
- package/dist/LatencyStore.d.ts +8 -3
- package/dist/LatencyStore.js +18 -0
- package/dist/LatencyStore.js.map +1 -1
- package/dist/MetricsRecorder.d.ts +34 -10
- package/dist/MetricsRecorder.js +30 -20
- package/dist/MetricsRecorder.js.map +1 -1
- package/dist/RedisHistoryAdmin.d.ts +95 -0
- package/dist/RedisHistoryAdmin.js +328 -0
- package/dist/RedisHistoryAdmin.js.map +1 -0
- package/dist/RedisMetricsHistoryProvider.d.ts +7 -17
- package/dist/RedisMetricsHistoryProvider.js +9 -136
- package/dist/RedisMetricsHistoryProvider.js.map +1 -1
- package/dist/RedisMetricsStore.d.ts +27 -0
- package/dist/RedisMetricsStore.js +42 -0
- package/dist/RedisMetricsStore.js.map +1 -0
- package/dist/StoreHistoryProvider.d.ts +27 -0
- package/dist/StoreHistoryProvider.js +127 -0
- package/dist/StoreHistoryProvider.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/jobSources.d.ts +97 -0
- package/dist/jobSources.js +248 -0
- package/dist/jobSources.js.map +1 -0
- package/dist/keys.d.ts +7 -0
- package/dist/keys.js +23 -1
- package/dist/keys.js.map +1 -1
- package/dist/postgres/PostgresMetricsHistoryProvider.d.ts +37 -0
- package/dist/postgres/PostgresMetricsHistoryProvider.js +30 -0
- package/dist/postgres/PostgresMetricsHistoryProvider.js.map +1 -0
- package/dist/postgres/PostgresMetricsStore.d.ts +65 -0
- package/dist/postgres/PostgresMetricsStore.js +88 -0
- package/dist/postgres/PostgresMetricsStore.js.map +1 -0
- package/dist/postgres/admin.d.ts +31 -0
- package/dist/postgres/admin.js +178 -0
- package/dist/postgres/admin.js.map +1 -0
- package/dist/postgres/connection.d.ts +53 -0
- package/dist/postgres/connection.js +54 -0
- package/dist/postgres/connection.js.map +1 -0
- package/dist/postgres/context.d.ts +29 -0
- package/dist/postgres/context.js +76 -0
- package/dist/postgres/context.js.map +1 -0
- package/dist/postgres/schema.d.ts +26 -0
- package/dist/postgres/schema.js +148 -0
- package/dist/postgres/schema.js.map +1 -0
- package/dist/postgres/stores.d.ts +51 -0
- package/dist/postgres/stores.js +252 -0
- package/dist/postgres/stores.js.map +1 -0
- package/dist/store.d.ts +80 -0
- package/dist/store.js +3 -0
- package/dist/store.js.map +1 -0
- package/package.json +13 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MetricsHistoryPoint, MetricsHistoryProvider, MetricsHistoryQuery, MetricsLatencyPoint, MetricsLatencyQuery } from '@worker-manager/api/typings/app';
|
|
2
|
+
import type { HistoryStats, PurgeOptions, PurgeResult } from './HistoryAdmin';
|
|
3
|
+
import type { MetricsStore, Retention } from './store';
|
|
4
|
+
/**
|
|
5
|
+
* The `MetricsHistoryProvider` contract over any `MetricsStore`. Everything that decides what
|
|
6
|
+
* a chart receives lives here, once: span clamping, day-start alignment, the empty-history
|
|
7
|
+
* rule and percentile merging. The stores only fetch buckets, so a Redis and a PostgreSQL
|
|
8
|
+
* board answer the same query with the same points.
|
|
9
|
+
*/
|
|
10
|
+
export declare class StoreHistoryProvider implements MetricsHistoryProvider {
|
|
11
|
+
private readonly counters;
|
|
12
|
+
private readonly latency;
|
|
13
|
+
private readonly admin;
|
|
14
|
+
private readonly retentionDays;
|
|
15
|
+
constructor(store: MetricsStore, retention: Retention);
|
|
16
|
+
/** Backs the board's storage panel. See MetricsHistoryAdmin.stats. */
|
|
17
|
+
getUsage(): Promise<HistoryStats>;
|
|
18
|
+
/** Backs the board's "clear history" action. See MetricsHistoryAdmin.purge. */
|
|
19
|
+
purge(options?: PurgeOptions): Promise<PurgeResult>;
|
|
20
|
+
getHistory(query: MetricsHistoryQuery): Promise<MetricsHistoryPoint[]>;
|
|
21
|
+
getLatency(query: MetricsLatencyQuery): Promise<MetricsLatencyPoint[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Clamps the span to the retention window so an unbounded `from` (e.g. 0) can't make
|
|
24
|
+
* dayRange produce an unbounded number of day buckets -- older data doesn't exist anyway.
|
|
25
|
+
*/
|
|
26
|
+
private daysFor;
|
|
27
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StoreHistoryProvider = void 0;
|
|
4
|
+
const histogram_1 = require("./histogram");
|
|
5
|
+
const keys_1 = require("./keys");
|
|
6
|
+
const MS_PER_HOUR = 3600000;
|
|
7
|
+
/**
|
|
8
|
+
* The `MetricsHistoryProvider` contract over any `MetricsStore`. Everything that decides what
|
|
9
|
+
* a chart receives lives here, once: span clamping, day-start alignment, the empty-history
|
|
10
|
+
* rule and percentile merging. The stores only fetch buckets, so a Redis and a PostgreSQL
|
|
11
|
+
* board answer the same query with the same points.
|
|
12
|
+
*/
|
|
13
|
+
class StoreHistoryProvider {
|
|
14
|
+
constructor(store, retention) {
|
|
15
|
+
this.retentionDays = retention.days;
|
|
16
|
+
this.counters = store.counterStore(retention);
|
|
17
|
+
this.latency = store.latencyStore(retention);
|
|
18
|
+
this.admin = store.administration();
|
|
19
|
+
}
|
|
20
|
+
/** Backs the board's storage panel. See MetricsHistoryAdmin.stats. */
|
|
21
|
+
async getUsage() {
|
|
22
|
+
return this.admin.stats();
|
|
23
|
+
}
|
|
24
|
+
/** Backs the board's "clear history" action. See MetricsHistoryAdmin.purge. */
|
|
25
|
+
async purge(options = {}) {
|
|
26
|
+
return this.admin.purge(options);
|
|
27
|
+
}
|
|
28
|
+
async getHistory(query) {
|
|
29
|
+
var _a;
|
|
30
|
+
const queue = (_a = query.queue) !== null && _a !== void 0 ? _a : keys_1.GLOBAL_QUEUE;
|
|
31
|
+
const days = this.daysFor(query);
|
|
32
|
+
if (query.metric === 'queueage') {
|
|
33
|
+
const ages = await this.latency.readQueueAge(queue, query.granularity, days);
|
|
34
|
+
// Day points are stamped at the day's start, so an intraday `from` would drop the day
|
|
35
|
+
// it falls in. Floored, exactly as the counter path below does it.
|
|
36
|
+
const lowerBound = query.granularity === 'day' ? dayFloor(query.from) : query.from;
|
|
37
|
+
return Object.keys(ages)
|
|
38
|
+
.map((key) => ({
|
|
39
|
+
ts: query.granularity === 'day' ? (0, keys_1.dayToStartMs)(key) : Number(key) * MS_PER_HOUR,
|
|
40
|
+
value: ages[key],
|
|
41
|
+
}))
|
|
42
|
+
.filter((p) => p.ts >= lowerBound && p.ts <= query.to)
|
|
43
|
+
.sort((a, b) => a.ts - b.ts);
|
|
44
|
+
}
|
|
45
|
+
if (query.granularity === 'day') {
|
|
46
|
+
const totals = await this.counters.readDailyTotals(queue, query.metric, days);
|
|
47
|
+
// Empty history only when no day in range was ever recorded (all fields missing).
|
|
48
|
+
// A day with a stored 0 still counts as recorded -- otherwise the UI's empty
|
|
49
|
+
// state would be unreachable once any data exists.
|
|
50
|
+
if (totals.every((value) => value == null)) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
return days
|
|
54
|
+
.map((day, i) => { var _a; return ({ ts: (0, keys_1.dayToStartMs)(day), value: (_a = totals[i]) !== null && _a !== void 0 ? _a : 0 }); })
|
|
55
|
+
.filter((p) => p.ts >= dayFloor(query.from) && p.ts <= query.to);
|
|
56
|
+
}
|
|
57
|
+
const hours = await this.counters.readHours(queue, query.metric, days);
|
|
58
|
+
return Object.keys(hours)
|
|
59
|
+
.map((field) => ({ ts: Number(field) * MS_PER_HOUR, value: hours[field] }))
|
|
60
|
+
.filter((p) => p.ts >= query.from && p.ts <= query.to)
|
|
61
|
+
.sort((a, b) => a.ts - b.ts);
|
|
62
|
+
}
|
|
63
|
+
async getLatency(query) {
|
|
64
|
+
var _a;
|
|
65
|
+
const queue = (_a = query.queue) !== null && _a !== void 0 ? _a : keys_1.GLOBAL_QUEUE;
|
|
66
|
+
const days = this.daysFor(query);
|
|
67
|
+
if (query.granularity === 'range') {
|
|
68
|
+
// Percentiles don't merge: averaging per-day p95s isn't the same number as the p95 of
|
|
69
|
+
// the whole range. Read the day tier's bucket vectors and merge them, then compute each
|
|
70
|
+
// requested percentile once from the summed vector.
|
|
71
|
+
const raw = await this.latency.readRange(queue, query.metric, 'day', days);
|
|
72
|
+
let merged = (0, histogram_1.emptyVector)();
|
|
73
|
+
for (const day of days) {
|
|
74
|
+
const vector = raw[day];
|
|
75
|
+
if (vector) {
|
|
76
|
+
merged = (0, histogram_1.mergeVectors)(merged, vector);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const count = (0, histogram_1.vectorTotal)(merged);
|
|
80
|
+
if (count === 0) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const values = {};
|
|
84
|
+
for (const p of query.percentiles) {
|
|
85
|
+
values[String(p)] = (0, histogram_1.quantile)(merged, p);
|
|
86
|
+
}
|
|
87
|
+
return [{ ts: query.from, count: Math.round(count), values }];
|
|
88
|
+
}
|
|
89
|
+
const raw = await this.latency.readRange(queue, query.metric, query.granularity, days);
|
|
90
|
+
// Same day-start alignment as getHistory: comparing a day bucket against a raw `from`
|
|
91
|
+
// would drop the oldest day and leave this chart one bucket shorter than the throughput
|
|
92
|
+
// chart drawn for the same range.
|
|
93
|
+
const lowerBound = query.granularity === 'day' ? dayFloor(query.from) : query.from;
|
|
94
|
+
const points = [];
|
|
95
|
+
for (const key of Object.keys(raw)) {
|
|
96
|
+
const ts = query.granularity === 'day' ? (0, keys_1.dayToStartMs)(key) : Number(key) * MS_PER_HOUR;
|
|
97
|
+
if (ts < lowerBound || ts > query.to) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const vector = raw[key];
|
|
101
|
+
const count = (0, histogram_1.vectorTotal)(vector);
|
|
102
|
+
if (count === 0) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const values = {};
|
|
106
|
+
for (const p of query.percentiles) {
|
|
107
|
+
values[String(p)] = (0, histogram_1.quantile)(vector, p);
|
|
108
|
+
}
|
|
109
|
+
points.push({ ts, count: Math.round(count), values });
|
|
110
|
+
}
|
|
111
|
+
return points.sort((a, b) => a.ts - b.ts);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Clamps the span to the retention window so an unbounded `from` (e.g. 0) can't make
|
|
115
|
+
* dayRange produce an unbounded number of day buckets -- older data doesn't exist anyway.
|
|
116
|
+
*/
|
|
117
|
+
daysFor(query) {
|
|
118
|
+
const maxSpanMs = (this.retentionDays + 1) * 86400000;
|
|
119
|
+
return (0, keys_1.dayRange)(Math.max(query.from, query.to - maxSpanMs), query.to);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.StoreHistoryProvider = StoreHistoryProvider;
|
|
123
|
+
function dayFloor(ms) {
|
|
124
|
+
const d = new Date(ms);
|
|
125
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=StoreHistoryProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreHistoryProvider.js","sourceRoot":"","sources":["../src/StoreHistoryProvider.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
export { MetricsHistoryAdmin } from './HistoryAdmin';
|
|
2
|
-
export type { HistoryQueueStats, HistoryStats, MetricsHistoryAdminOptions, PurgeOptions, PurgeResult, } from './HistoryAdmin';
|
|
2
|
+
export type { HistoryQueueStats, HistoryStats, HistoryTier, MetricsHistoryAdminOptions, PurgeOptions, PurgeResult, TierStats, } from './HistoryAdmin';
|
|
3
3
|
export { MetricsRecorder } from './MetricsRecorder';
|
|
4
4
|
export type { MetricsRecorderOptions } from './MetricsRecorder';
|
|
5
|
-
export type { Retention } from './
|
|
5
|
+
export type { MetricsStore, Retention } from './store';
|
|
6
|
+
export { RedisMetricsStore } from './RedisMetricsStore';
|
|
7
|
+
export type { RedisMetricsStoreOptions } from './RedisMetricsStore';
|
|
6
8
|
export { RedisMetricsHistoryProvider } from './RedisMetricsHistoryProvider';
|
|
7
9
|
export type { RedisMetricsHistoryProviderOptions } from './RedisMetricsHistoryProvider';
|
|
10
|
+
export { PostgresMetricsStore, migratePostgresMetrics } from './postgres/PostgresMetricsStore';
|
|
11
|
+
export type { MigratePostgresMetricsOptions, PostgresMetricsStoreOptions, } from './postgres/PostgresMetricsStore';
|
|
12
|
+
export { PostgresMetricsHistoryProvider } from './postgres/PostgresMetricsHistoryProvider';
|
|
13
|
+
export type { PostgresMetricsHistoryProviderOptions } from './postgres/PostgresMetricsHistoryProvider';
|
|
14
|
+
export type { PgPool, PostgresConnection, PostgresPoolConfig } from './postgres/connection';
|
|
15
|
+
export { SCHEMA_VERSION as POSTGRES_METRICS_SCHEMA_VERSION } from './postgres/schema';
|
|
8
16
|
export { LatencySampler } from './LatencySampler';
|
|
9
17
|
export type { LatencySamplerOptions } from './LatencySampler';
|
|
10
18
|
export { LatencyStore, QUEUE_AGE_METRIC } from './LatencyStore';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.quantile = exports.BUCKET_COUNT = exports.BUCKET_BOUNDS = exports.QUEUE_AGE_METRIC = exports.LatencyStore = exports.LatencySampler = exports.RedisMetricsHistoryProvider = exports.MetricsRecorder = exports.MetricsHistoryAdmin = void 0;
|
|
3
|
+
exports.quantile = exports.BUCKET_COUNT = exports.BUCKET_BOUNDS = exports.QUEUE_AGE_METRIC = exports.LatencyStore = exports.LatencySampler = exports.POSTGRES_METRICS_SCHEMA_VERSION = exports.PostgresMetricsHistoryProvider = exports.migratePostgresMetrics = exports.PostgresMetricsStore = exports.RedisMetricsHistoryProvider = exports.RedisMetricsStore = exports.MetricsRecorder = exports.MetricsHistoryAdmin = void 0;
|
|
4
4
|
var HistoryAdmin_1 = require("./HistoryAdmin");
|
|
5
5
|
Object.defineProperty(exports, "MetricsHistoryAdmin", { enumerable: true, get: function () { return HistoryAdmin_1.MetricsHistoryAdmin; } });
|
|
6
6
|
var MetricsRecorder_1 = require("./MetricsRecorder");
|
|
7
7
|
Object.defineProperty(exports, "MetricsRecorder", { enumerable: true, get: function () { return MetricsRecorder_1.MetricsRecorder; } });
|
|
8
|
+
var RedisMetricsStore_1 = require("./RedisMetricsStore");
|
|
9
|
+
Object.defineProperty(exports, "RedisMetricsStore", { enumerable: true, get: function () { return RedisMetricsStore_1.RedisMetricsStore; } });
|
|
8
10
|
var RedisMetricsHistoryProvider_1 = require("./RedisMetricsHistoryProvider");
|
|
9
11
|
Object.defineProperty(exports, "RedisMetricsHistoryProvider", { enumerable: true, get: function () { return RedisMetricsHistoryProvider_1.RedisMetricsHistoryProvider; } });
|
|
12
|
+
var PostgresMetricsStore_1 = require("./postgres/PostgresMetricsStore");
|
|
13
|
+
Object.defineProperty(exports, "PostgresMetricsStore", { enumerable: true, get: function () { return PostgresMetricsStore_1.PostgresMetricsStore; } });
|
|
14
|
+
Object.defineProperty(exports, "migratePostgresMetrics", { enumerable: true, get: function () { return PostgresMetricsStore_1.migratePostgresMetrics; } });
|
|
15
|
+
var PostgresMetricsHistoryProvider_1 = require("./postgres/PostgresMetricsHistoryProvider");
|
|
16
|
+
Object.defineProperty(exports, "PostgresMetricsHistoryProvider", { enumerable: true, get: function () { return PostgresMetricsHistoryProvider_1.PostgresMetricsHistoryProvider; } });
|
|
17
|
+
var schema_1 = require("./postgres/schema");
|
|
18
|
+
Object.defineProperty(exports, "POSTGRES_METRICS_SCHEMA_VERSION", { enumerable: true, get: function () { return schema_1.SCHEMA_VERSION; } });
|
|
10
19
|
var LatencySampler_1 = require("./LatencySampler");
|
|
11
20
|
Object.defineProperty(exports, "LatencySampler", { enumerable: true, get: function () { return LatencySampler_1.LatencySampler; } });
|
|
12
21
|
var LatencyStore_1 = require("./LatencyStore");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+CAAqD;AAA5C,mHAAA,mBAAmB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+CAAqD;AAA5C,mHAAA,mBAAmB,OAAA;AAU5B,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AAGxB,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAE1B,6EAA4E;AAAnE,0IAAA,2BAA2B,OAAA;AAEpC,wEAA+F;AAAtF,4HAAA,oBAAoB,OAAA;AAAE,8HAAA,sBAAsB,OAAA;AAKrD,4FAA2F;AAAlF,gJAAA,8BAA8B,OAAA;AAGvC,4CAAsF;AAA7E,yHAAA,cAAc,OAAmC;AAC1D,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AAEvB,+CAAgE;AAAvD,4GAAA,YAAY,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAEvC,yCAAoE;AAA3D,0GAAA,aAAa,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,qGAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { BaseAdapter } from '@worker-manager/api/baseAdapter';
|
|
2
|
+
import type { MetricsClient } from './connection';
|
|
3
|
+
import { type PgQueryable } from './postgres/connection';
|
|
4
|
+
/** The timing fields of one finished job, as the latency sampler needs them. */
|
|
5
|
+
export interface FinishedJob {
|
|
6
|
+
/** Creation time, `null` when the job carries none. */
|
|
7
|
+
timestamp: number | null;
|
|
8
|
+
processedOn: number;
|
|
9
|
+
finishedOn: number;
|
|
10
|
+
/** Attempts made, counting the one that finished. */
|
|
11
|
+
attempts: number;
|
|
12
|
+
}
|
|
13
|
+
export interface FinishedJobs {
|
|
14
|
+
/** Jobs that finished in the range, before any subsampling. */
|
|
15
|
+
total: number;
|
|
16
|
+
/** How many of them were selected for reading. `total / sampled` scales counts back up. */
|
|
17
|
+
sampled: number;
|
|
18
|
+
jobs: FinishedJob[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Where the sampler reads job timings from: the queue's own datastore, which is independent
|
|
22
|
+
* of where history is stored. A PostgreSQL-backed queue can record into Redis and vice versa.
|
|
23
|
+
*/
|
|
24
|
+
export interface JobSource {
|
|
25
|
+
/**
|
|
26
|
+
* Jobs whose finish time is in `(afterMs, upToMs]`. Above `maxSamples` a uniform subset is
|
|
27
|
+
* read rather than the first N, which would bias towards the start of the range.
|
|
28
|
+
*/
|
|
29
|
+
finishedJobs(afterMs: number, upToMs: number, maxSamples: number): Promise<FinishedJobs>;
|
|
30
|
+
/**
|
|
31
|
+
* Age of the oldest waiting job, 0 for an empty backlog, and `null` when the backlog could
|
|
32
|
+
* not be read cleanly: a gap in the series reads as "not measured", a zero as "healthy".
|
|
33
|
+
*/
|
|
34
|
+
oldestWaitingAge(now: number): Promise<number | null>;
|
|
35
|
+
}
|
|
36
|
+
interface AdapterWithKeys extends BaseAdapter {
|
|
37
|
+
getQueueKey(set: string): string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A BullMQ queue stored in Redis, read with the same commands BullMQ itself writes.
|
|
41
|
+
*
|
|
42
|
+
* BullMQ's moveToFinished does `ZADD targetSet, timestamp, jobId` and writes the same value to
|
|
43
|
+
* finishedOn, so the completed and failed sets are sorted sets scored by finish time.
|
|
44
|
+
*/
|
|
45
|
+
export declare class RedisJobSource implements JobSource {
|
|
46
|
+
private readonly redis;
|
|
47
|
+
private readonly adapter;
|
|
48
|
+
constructor(redis: MetricsClient, adapter: AdapterWithKeys);
|
|
49
|
+
finishedJobs(afterMs: number, upToMs: number, maxSamples: number): Promise<FinishedJobs>;
|
|
50
|
+
/**
|
|
51
|
+
* The backlog is not all in one place. `wait` holds it while the queue is running, but
|
|
52
|
+
* pausing RENAMEs that list to `paused` and routes new jobs there, and anything added with
|
|
53
|
+
* a priority goes to the `prioritized` sorted set instead, which can leave `wait`
|
|
54
|
+
* permanently empty. Reading only `wait` reports a healthy zero for a queue that is badly
|
|
55
|
+
* backed up, which is the opposite of what this gauge is for, so all three are consulted
|
|
56
|
+
* and the worst age wins.
|
|
57
|
+
*/
|
|
58
|
+
oldestWaitingAge(now: number): Promise<number | null>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A BullMQ v6 queue stored in PostgreSQL. BullMQ keeps every job in one `job` table with a
|
|
62
|
+
* `state` column, and indexes finished jobs on `(queue, state, finished_at_ms)`, which is the
|
|
63
|
+
* exact shape of the range scan below.
|
|
64
|
+
*/
|
|
65
|
+
export declare class PostgresJobSource implements JobSource {
|
|
66
|
+
private readonly pool;
|
|
67
|
+
private readonly queueName;
|
|
68
|
+
private readonly job;
|
|
69
|
+
constructor(pool: PgQueryable, schema: string, queueName: string);
|
|
70
|
+
/**
|
|
71
|
+
* One round trip. The selection is the same evenly spaced pick the Redis source makes,
|
|
72
|
+
* done in SQL so a busy tick transfers at most `maxSamples` rows: row `i` of `total` is
|
|
73
|
+
* kept when it is `floor(k * total / max)` for some `k`, which integer arithmetic decides
|
|
74
|
+
* without enumerating the `k`s.
|
|
75
|
+
*/
|
|
76
|
+
finishedJobs(afterMs: number, upToMs: number, maxSamples: number): Promise<FinishedJobs>;
|
|
77
|
+
/**
|
|
78
|
+
* Paused and prioritized are not physical states here: a paused queue's jobs stay
|
|
79
|
+
* `waiting`, and a prioritized job is a waiting job with `priority > 0`. The ready index
|
|
80
|
+
* orders waiting jobs by `(priority, seq)`, so the three ends read below are index probes,
|
|
81
|
+
* not a scan of the backlog: the next job to run, the far end of the prioritized range, and
|
|
82
|
+
* the far end of the unprioritized range, which holds the oldest job of a LIFO queue.
|
|
83
|
+
* Like the Redis prioritized read, this is an approximation that never reads the whole set.
|
|
84
|
+
*/
|
|
85
|
+
oldestWaitingAge(now: number): Promise<number | null>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The PostgreSQL pool behind a BullMQ v6 queue, or `null` for anything else.
|
|
89
|
+
*
|
|
90
|
+
* This is a structural probe of BullMQAdapter's queue and BullMQ's backend rather than a
|
|
91
|
+
* public API of either, so every step is optional and a shape it does not recognise simply
|
|
92
|
+
* reads as "not PostgreSQL". The pool is used directly rather than through the backend's
|
|
93
|
+
* `query()`, which parks a query forever once the queue starts closing and would leave a
|
|
94
|
+
* recorder tick hanging on a queue that was just removed from the board.
|
|
95
|
+
*/
|
|
96
|
+
export declare function postgresSourceOf(adapter: BaseAdapter): PostgresJobSource | null;
|
|
97
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresJobSource = exports.RedisJobSource = void 0;
|
|
4
|
+
exports.postgresSourceOf = postgresSourceOf;
|
|
5
|
+
const connection_1 = require("./postgres/connection");
|
|
6
|
+
/**
|
|
7
|
+
* A BullMQ queue stored in Redis, read with the same commands BullMQ itself writes.
|
|
8
|
+
*
|
|
9
|
+
* BullMQ's moveToFinished does `ZADD targetSet, timestamp, jobId` and writes the same value to
|
|
10
|
+
* finishedOn, so the completed and failed sets are sorted sets scored by finish time.
|
|
11
|
+
*/
|
|
12
|
+
class RedisJobSource {
|
|
13
|
+
constructor(redis, adapter) {
|
|
14
|
+
this.redis = redis;
|
|
15
|
+
this.adapter = adapter;
|
|
16
|
+
}
|
|
17
|
+
async finishedJobs(afterMs, upToMs, maxSamples) {
|
|
18
|
+
var _a;
|
|
19
|
+
const ids = [];
|
|
20
|
+
for (const set of ['completed', 'failed']) {
|
|
21
|
+
const found = await this.redis.zrangebyscore(this.adapter.getQueueKey(set), `(${afterMs}`, upToMs);
|
|
22
|
+
ids.push(...found);
|
|
23
|
+
}
|
|
24
|
+
if (ids.length === 0) {
|
|
25
|
+
return { total: 0, sampled: 0, jobs: [] };
|
|
26
|
+
}
|
|
27
|
+
// The id list is one cheap round trip; the HMGETs are the real cost.
|
|
28
|
+
const selected = ids.length > maxSamples ? sampleUniformly(ids, maxSamples) : ids;
|
|
29
|
+
const pipeline = this.redis.pipeline();
|
|
30
|
+
for (const id of selected) {
|
|
31
|
+
pipeline.hmget(this.adapter.getQueueKey(String(id)), 'timestamp', 'processedOn', 'finishedOn',
|
|
32
|
+
// BullMQ 5 counts attempts in `atm`; `attemptsMade` is the pre-v5 name and is read
|
|
33
|
+
// as a fallback exactly the way Job.fromJSON does.
|
|
34
|
+
'atm', 'attemptsMade');
|
|
35
|
+
}
|
|
36
|
+
const rows = await pipeline.exec();
|
|
37
|
+
const jobs = [];
|
|
38
|
+
for (const row of rows !== null && rows !== void 0 ? rows : []) {
|
|
39
|
+
const values = row === null || row === void 0 ? void 0 : row[1];
|
|
40
|
+
if (!values) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const [timestamp, processedOn, finishedOn, atm, attemptsMade] = values;
|
|
44
|
+
if (!processedOn || !finishedOn) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
jobs.push({
|
|
48
|
+
timestamp: timestamp ? Number(timestamp) : null,
|
|
49
|
+
processedOn: Number(processedOn),
|
|
50
|
+
finishedOn: Number(finishedOn),
|
|
51
|
+
attempts: Number((_a = atm !== null && atm !== void 0 ? atm : attemptsMade) !== null && _a !== void 0 ? _a : 0),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return { total: ids.length, sampled: selected.length, jobs };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The backlog is not all in one place. `wait` holds it while the queue is running, but
|
|
58
|
+
* pausing RENAMEs that list to `paused` and routes new jobs there, and anything added with
|
|
59
|
+
* a priority goes to the `prioritized` sorted set instead, which can leave `wait`
|
|
60
|
+
* permanently empty. Reading only `wait` reports a healthy zero for a queue that is badly
|
|
61
|
+
* backed up, which is the opposite of what this gauge is for, so all three are consulted
|
|
62
|
+
* and the worst age wins.
|
|
63
|
+
*/
|
|
64
|
+
async oldestWaitingAge(now) {
|
|
65
|
+
var _a;
|
|
66
|
+
const candidates = await this.redis
|
|
67
|
+
.pipeline()
|
|
68
|
+
// BullMQ LPUSHes to the wait list and workers RPOPLPUSH from it, so the tail is oldest.
|
|
69
|
+
.lrange(this.adapter.getQueueKey('wait'), -1, -1)
|
|
70
|
+
.lrange(this.adapter.getQueueKey('paused'), -1, -1)
|
|
71
|
+
// The prioritized set is scored by priority, not by time, so neither end is guaranteed
|
|
72
|
+
// to hold the oldest job. Both ends are an approximation, and a cheap one: the true
|
|
73
|
+
// oldest would mean fetching the whole set every tick.
|
|
74
|
+
// String indices: ioredis v6 types `zrange`'s stop arg as string-only (Redis coerces
|
|
75
|
+
// either way), so numeric literals no longer typecheck. '0'/'-1' are the first/last members.
|
|
76
|
+
.zrange(this.adapter.getQueueKey('prioritized'), '0', '0')
|
|
77
|
+
.zrange(this.adapter.getQueueKey('prioritized'), '-1', '-1')
|
|
78
|
+
.exec();
|
|
79
|
+
// A pipeline reports failures per command: a Redis error arrives in the entry's error
|
|
80
|
+
// slot next to a null result, rather than as a throw. Reading only the result slot turns
|
|
81
|
+
// a failed read into an empty backlog and records an age of 0, which is the most
|
|
82
|
+
// reassuring number this gauge can produce, at the moment it is least entitled to.
|
|
83
|
+
if (!candidates || candidates.some(([error]) => error)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const ids = new Set();
|
|
87
|
+
for (const entry of candidates) {
|
|
88
|
+
for (const id of (_a = entry[1]) !== null && _a !== void 0 ? _a : []) {
|
|
89
|
+
ids.add(String(id));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (ids.size === 0) {
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
const stamps = this.redis.pipeline();
|
|
96
|
+
for (const id of ids) {
|
|
97
|
+
stamps.hget(this.adapter.getQueueKey(id), 'timestamp');
|
|
98
|
+
}
|
|
99
|
+
const rows = await stamps.exec();
|
|
100
|
+
// Same reasoning: a failed timestamp read does not zero the gauge, it understates it,
|
|
101
|
+
// which is the same wrong-but-reassuring answer in a quieter form.
|
|
102
|
+
if (!rows || rows.some(([error]) => error)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
let oldest = 0;
|
|
106
|
+
for (const row of rows) {
|
|
107
|
+
const raw = row[1];
|
|
108
|
+
if (!raw) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const enqueuedAt = Number(raw);
|
|
112
|
+
if (!Number.isFinite(enqueuedAt)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
oldest = Math.max(oldest, now - enqueuedAt);
|
|
116
|
+
}
|
|
117
|
+
return Math.max(0, oldest);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.RedisJobSource = RedisJobSource;
|
|
121
|
+
/**
|
|
122
|
+
* A BullMQ v6 queue stored in PostgreSQL. BullMQ keeps every job in one `job` table with a
|
|
123
|
+
* `state` column, and indexes finished jobs on `(queue, state, finished_at_ms)`, which is the
|
|
124
|
+
* exact shape of the range scan below.
|
|
125
|
+
*/
|
|
126
|
+
class PostgresJobSource {
|
|
127
|
+
constructor(pool, schema, queueName) {
|
|
128
|
+
this.pool = pool;
|
|
129
|
+
this.queueName = queueName;
|
|
130
|
+
this.job = `${(0, connection_1.quoteIdentifier)(schema)}.job`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* One round trip. The selection is the same evenly spaced pick the Redis source makes,
|
|
134
|
+
* done in SQL so a busy tick transfers at most `maxSamples` rows: row `i` of `total` is
|
|
135
|
+
* kept when it is `floor(k * total / max)` for some `k`, which integer arithmetic decides
|
|
136
|
+
* without enumerating the `k`s.
|
|
137
|
+
*/
|
|
138
|
+
async finishedJobs(afterMs, upToMs, maxSamples) {
|
|
139
|
+
var _a;
|
|
140
|
+
const max = Math.max(1, Math.floor(maxSamples));
|
|
141
|
+
const { rows } = await this.pool.query(`WITH finished AS (
|
|
142
|
+
SELECT added_at_ms, processed_at_ms, finished_at_ms, attempts_made,
|
|
143
|
+
row_number() OVER (ORDER BY finished_at_ms, id) - 1 AS i,
|
|
144
|
+
count(*) OVER () AS total
|
|
145
|
+
FROM ${this.job}
|
|
146
|
+
WHERE queue = $1
|
|
147
|
+
AND state IN ('completed', 'failed')
|
|
148
|
+
AND finished_at_ms > $2
|
|
149
|
+
AND finished_at_ms <= $3
|
|
150
|
+
)
|
|
151
|
+
SELECT added_at_ms, processed_at_ms, finished_at_ms, attempts_made, total
|
|
152
|
+
FROM finished
|
|
153
|
+
WHERE total <= $4
|
|
154
|
+
OR ((i * $4 + total - 1) / total) * total / $4 = i`, [this.queueName, afterMs, upToMs, max]);
|
|
155
|
+
if (rows.length === 0) {
|
|
156
|
+
return { total: 0, sampled: 0, jobs: [] };
|
|
157
|
+
}
|
|
158
|
+
const total = Number(rows[0].total);
|
|
159
|
+
const jobs = [];
|
|
160
|
+
for (const row of rows) {
|
|
161
|
+
if (row.processed_at_ms == null || row.finished_at_ms == null) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
jobs.push({
|
|
165
|
+
timestamp: row.added_at_ms == null ? null : Number(row.added_at_ms),
|
|
166
|
+
processedOn: Number(row.processed_at_ms),
|
|
167
|
+
finishedOn: Number(row.finished_at_ms),
|
|
168
|
+
attempts: Number((_a = row.attempts_made) !== null && _a !== void 0 ? _a : 0),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return { total, sampled: rows.length, jobs };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Paused and prioritized are not physical states here: a paused queue's jobs stay
|
|
175
|
+
* `waiting`, and a prioritized job is a waiting job with `priority > 0`. The ready index
|
|
176
|
+
* orders waiting jobs by `(priority, seq)`, so the three ends read below are index probes,
|
|
177
|
+
* not a scan of the backlog: the next job to run, the far end of the prioritized range, and
|
|
178
|
+
* the far end of the unprioritized range, which holds the oldest job of a LIFO queue.
|
|
179
|
+
* Like the Redis prioritized read, this is an approximation that never reads the whole set.
|
|
180
|
+
*/
|
|
181
|
+
async oldestWaitingAge(now) {
|
|
182
|
+
var _a;
|
|
183
|
+
const { rows } = await this.pool.query(`SELECT min(added_at_ms) AS oldest FROM (
|
|
184
|
+
(SELECT added_at_ms FROM ${this.job}
|
|
185
|
+
WHERE queue = $1 AND state = 'waiting' ORDER BY priority, seq LIMIT 1)
|
|
186
|
+
UNION ALL
|
|
187
|
+
(SELECT added_at_ms FROM ${this.job}
|
|
188
|
+
WHERE queue = $1 AND state = 'waiting' ORDER BY priority DESC, seq DESC LIMIT 1)
|
|
189
|
+
UNION ALL
|
|
190
|
+
(SELECT added_at_ms FROM ${this.job}
|
|
191
|
+
WHERE queue = $1 AND state = 'waiting' AND priority = 0 ORDER BY seq DESC LIMIT 1)
|
|
192
|
+
) AS ends`, [this.queueName]);
|
|
193
|
+
const oldest = (_a = rows[0]) === null || _a === void 0 ? void 0 : _a.oldest;
|
|
194
|
+
if (oldest == null) {
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
return Math.max(0, now - Number(oldest));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.PostgresJobSource = PostgresJobSource;
|
|
201
|
+
/**
|
|
202
|
+
* The PostgreSQL pool behind a BullMQ v6 queue, or `null` for anything else.
|
|
203
|
+
*
|
|
204
|
+
* This is a structural probe of BullMQAdapter's queue and BullMQ's backend rather than a
|
|
205
|
+
* public API of either, so every step is optional and a shape it does not recognise simply
|
|
206
|
+
* reads as "not PostgreSQL". The pool is used directly rather than through the backend's
|
|
207
|
+
* `query()`, which parks a query forever once the queue starts closing and would leave a
|
|
208
|
+
* recorder tick hanging on a queue that was just removed from the board.
|
|
209
|
+
*/
|
|
210
|
+
function postgresSourceOf(adapter) {
|
|
211
|
+
var _a, _b, _c, _d, _e;
|
|
212
|
+
const queue = adapter.queue;
|
|
213
|
+
if (!queue || typeof queue.getBackend !== 'function') {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
let backend;
|
|
217
|
+
try {
|
|
218
|
+
backend = queue.getBackend();
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const pool = (_a = backend === null || backend === void 0 ? void 0 : backend.connection) === null || _a === void 0 ? void 0 : _a.pool;
|
|
224
|
+
if (!pool || typeof pool.query !== 'function') {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const schema = (_d = (_b = backend === null || backend === void 0 ? void 0 : backend.schema) !== null && _b !== void 0 ? _b : (_c = backend === null || backend === void 0 ? void 0 : backend.connection) === null || _c === void 0 ? void 0 : _c.schema) !== null && _d !== void 0 ? _d : 'bullmq';
|
|
228
|
+
const name = (_e = backend === null || backend === void 0 ? void 0 : backend.queueName) !== null && _e !== void 0 ? _e : queue.name;
|
|
229
|
+
if (typeof schema !== 'string' || typeof name !== 'string') {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
return new PostgresJobSource(pool, schema, name);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return null; // a schema name BullMQ itself would have refused
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Evenly spaced pick across the list, which preserves the distribution's shape. */
|
|
240
|
+
function sampleUniformly(ids, target) {
|
|
241
|
+
const stride = ids.length / target;
|
|
242
|
+
const out = [];
|
|
243
|
+
for (let i = 0; i < target; i++) {
|
|
244
|
+
out.push(ids[Math.floor(i * stride)]);
|
|
245
|
+
}
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=jobSources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobSources.js","sourceRoot":"","sources":["../src/jobSources.ts"],"names":[],"mappings":"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"}
|
package/dist/keys.d.ts
CHANGED
|
@@ -2,11 +2,16 @@ export declare const DEFAULT_NAMESPACE = "bull-board:metrics";
|
|
|
2
2
|
export declare const GLOBAL_QUEUE = "__global__";
|
|
3
3
|
/** Marks the hourly rollup key so it can't be mistaken for a minute-level day hash. */
|
|
4
4
|
export declare const HOUR_TIER = "hour";
|
|
5
|
+
export declare const DAY_PATTERN: RegExp;
|
|
6
|
+
export declare function msToDay(ms: number): string;
|
|
5
7
|
export declare function minuteToDay(minute: number): string;
|
|
6
8
|
/** Absolute hour index, the hourly counterpart of the absolute minute index. */
|
|
7
9
|
export declare function minuteToHour(minute: number): number;
|
|
8
10
|
export declare function shiftDay(day: string, offsetDays: number): string;
|
|
9
11
|
export declare function dayToStartMs(day: string): number;
|
|
12
|
+
/** Days since the epoch, the integer form of an ISO day that SQL buckets use. */
|
|
13
|
+
export declare function dayToIndex(day: string): number;
|
|
14
|
+
export declare function indexToDay(index: number): string;
|
|
10
15
|
export declare function dayRange(fromMs: number, toMs: number): string[];
|
|
11
16
|
export declare function resolveNamespace(prefix: string | undefined, clustered: boolean): string;
|
|
12
17
|
export interface MetricsKeys {
|
|
@@ -19,3 +24,5 @@ export interface MetricsKeys {
|
|
|
19
24
|
scanPattern: string;
|
|
20
25
|
}
|
|
21
26
|
export declare function metricsKeys(namespace: string): MetricsKeys;
|
|
27
|
+
/** An ISO `YYYY-MM-DD` day, validated, from a day string or a Date (UTC). */
|
|
28
|
+
export declare function toDay(value: Date | string): string;
|