@miragon/pulse-analytics-client 0.0.0-stage → 0.21.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/LICENSE +21 -0
- package/README.md +84 -2
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/metric-names.d.ts +43 -0
- package/dist/metric-names.d.ts.map +1 -0
- package/dist/metric-names.js +53 -0
- package/dist/metric-names.js.map +1 -0
- package/dist/metrics-contract-labels.test.d.ts +2 -0
- package/dist/metrics-contract-labels.test.d.ts.map +1 -0
- package/dist/metrics-contract-labels.test.js +169 -0
- package/dist/metrics-contract-labels.test.js.map +1 -0
- package/dist/metrics-contract.test.d.ts +2 -0
- package/dist/metrics-contract.test.d.ts.map +1 -0
- package/dist/metrics-contract.test.js +247 -0
- package/dist/metrics-contract.test.js.map +1 -0
- package/dist/prometheus-client.test.d.ts +2 -0
- package/dist/prometheus-client.test.d.ts.map +1 -0
- package/dist/prometheus-client.test.js +265 -0
- package/dist/prometheus-client.test.js.map +1 -0
- package/dist/prometheus.d.ts +100 -0
- package/dist/prometheus.d.ts.map +1 -0
- package/dist/prometheus.js +295 -0
- package/dist/prometheus.js.map +1 -0
- package/dist/prometheus.test.d.ts +2 -0
- package/dist/prometheus.test.d.ts.map +1 -0
- package/dist/prometheus.test.js +54 -0
- package/dist/prometheus.test.js.map +1 -0
- package/dist/promql-labels.test-support.d.ts +53 -0
- package/dist/promql-labels.test-support.d.ts.map +1 -0
- package/dist/promql-labels.test-support.js +237 -0
- package/dist/promql-labels.test-support.js.map +1 -0
- package/dist/promql-labels.test.d.ts +2 -0
- package/dist/promql-labels.test.d.ts.map +1 -0
- package/dist/promql-labels.test.js +190 -0
- package/dist/promql-labels.test.js.map +1 -0
- package/dist/promql-parse.test-support.d.ts +57 -0
- package/dist/promql-parse.test-support.d.ts.map +1 -0
- package/dist/promql-parse.test-support.js +284 -0
- package/dist/promql-parse.test-support.js.map +1 -0
- package/dist/promql-reading-rules.test-support.d.ts +29 -0
- package/dist/promql-reading-rules.test-support.d.ts.map +1 -0
- package/dist/promql-reading-rules.test-support.js +79 -0
- package/dist/promql-reading-rules.test-support.js.map +1 -0
- package/dist/promql-reading-rules.test.d.ts +2 -0
- package/dist/promql-reading-rules.test.d.ts.map +1 -0
- package/dist/promql-reading-rules.test.js +61 -0
- package/dist/promql-reading-rules.test.js.map +1 -0
- package/dist/promql-sources.test-support.d.ts +29 -0
- package/dist/promql-sources.test-support.d.ts.map +1 -0
- package/dist/promql-sources.test-support.js +112 -0
- package/dist/promql-sources.test-support.js.map +1 -0
- package/dist/promql-sources.test.d.ts +2 -0
- package/dist/promql-sources.test.d.ts.map +1 -0
- package/dist/promql-sources.test.js +93 -0
- package/dist/promql-sources.test.js.map +1 -0
- package/dist/queries/as-of.test.d.ts +2 -0
- package/dist/queries/as-of.test.d.ts.map +1 -0
- package/dist/queries/as-of.test.js +80 -0
- package/dist/queries/as-of.test.js.map +1 -0
- package/dist/queries/cluster-compare.d.ts +62 -0
- package/dist/queries/cluster-compare.d.ts.map +1 -0
- package/dist/queries/cluster-compare.js +70 -0
- package/dist/queries/cluster-compare.js.map +1 -0
- package/dist/queries/cluster-compare.test.d.ts +2 -0
- package/dist/queries/cluster-compare.test.d.ts.map +1 -0
- package/dist/queries/cluster-compare.test.js +81 -0
- package/dist/queries/cluster-compare.test.js.map +1 -0
- package/dist/queries/dashboard.d.ts +60 -0
- package/dist/queries/dashboard.d.ts.map +1 -0
- package/dist/queries/dashboard.js +292 -0
- package/dist/queries/dashboard.js.map +1 -0
- package/dist/queries/dashboard.test.d.ts +2 -0
- package/dist/queries/dashboard.test.d.ts.map +1 -0
- package/dist/queries/dashboard.test.js +423 -0
- package/dist/queries/dashboard.test.js.map +1 -0
- package/dist/queries/element.d.ts +68 -0
- package/dist/queries/element.d.ts.map +1 -0
- package/dist/queries/element.js +98 -0
- package/dist/queries/element.js.map +1 -0
- package/dist/queries/element.test.d.ts +2 -0
- package/dist/queries/element.test.d.ts.map +1 -0
- package/dist/queries/element.test.js +105 -0
- package/dist/queries/element.test.js.map +1 -0
- package/dist/queries/engine-compare.d.ts +52 -0
- package/dist/queries/engine-compare.d.ts.map +1 -0
- package/dist/queries/engine-compare.js +57 -0
- package/dist/queries/engine-compare.js.map +1 -0
- package/dist/queries/engine-compare.test.d.ts +2 -0
- package/dist/queries/engine-compare.test.d.ts.map +1 -0
- package/dist/queries/engine-compare.test.js +159 -0
- package/dist/queries/engine-compare.test.js.map +1 -0
- package/dist/queries/engine-landscape.d.ts +92 -0
- package/dist/queries/engine-landscape.d.ts.map +1 -0
- package/dist/queries/engine-landscape.js +183 -0
- package/dist/queries/engine-landscape.js.map +1 -0
- package/dist/queries/engine-landscape.test.d.ts +2 -0
- package/dist/queries/engine-landscape.test.d.ts.map +1 -0
- package/dist/queries/engine-landscape.test.js +171 -0
- package/dist/queries/engine-landscape.test.js.map +1 -0
- package/dist/queries/failures.d.ts +33 -0
- package/dist/queries/failures.d.ts.map +1 -0
- package/dist/queries/failures.js +31 -0
- package/dist/queries/failures.js.map +1 -0
- package/dist/queries/health.d.ts +77 -0
- package/dist/queries/health.d.ts.map +1 -0
- package/dist/queries/health.js +126 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/health.test.d.ts +2 -0
- package/dist/queries/health.test.d.ts.map +1 -0
- package/dist/queries/health.test.js +187 -0
- package/dist/queries/health.test.js.map +1 -0
- package/dist/queries/helpers.d.ts +184 -0
- package/dist/queries/helpers.d.ts.map +1 -0
- package/dist/queries/helpers.js +219 -0
- package/dist/queries/helpers.js.map +1 -0
- package/dist/queries/helpers.test.d.ts +2 -0
- package/dist/queries/helpers.test.d.ts.map +1 -0
- package/dist/queries/helpers.test.js +53 -0
- package/dist/queries/helpers.test.js.map +1 -0
- package/dist/queries/index.d.ts +19 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +10 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/performance.d.ts +103 -0
- package/dist/queries/performance.d.ts.map +1 -0
- package/dist/queries/performance.js +173 -0
- package/dist/queries/performance.js.map +1 -0
- package/dist/queries/performance.test.d.ts +2 -0
- package/dist/queries/performance.test.d.ts.map +1 -0
- package/dist/queries/performance.test.js +265 -0
- package/dist/queries/performance.test.js.map +1 -0
- package/dist/queries/version-compare.d.ts +41 -0
- package/dist/queries/version-compare.d.ts.map +1 -0
- package/dist/queries/version-compare.js +44 -0
- package/dist/queries/version-compare.js.map +1 -0
- package/dist/queries/version-compare.test.d.ts +2 -0
- package/dist/queries/version-compare.test.d.ts.map +1 -0
- package/dist/queries/version-compare.test.js +162 -0
- package/dist/queries/version-compare.test.js.map +1 -0
- package/dist/queries/windows.d.ts +40 -0
- package/dist/queries/windows.d.ts.map +1 -0
- package/dist/queries/windows.js +56 -0
- package/dist/queries/windows.js.map +1 -0
- package/dist/query-honesty.test.d.ts +2 -0
- package/dist/query-honesty.test.d.ts.map +1 -0
- package/dist/query-honesty.test.js +294 -0
- package/dist/query-honesty.test.js.map +1 -0
- package/dist/query-scenarios.test-support.d.ts +49 -0
- package/dist/query-scenarios.test-support.d.ts.map +1 -0
- package/dist/query-scenarios.test-support.js +146 -0
- package/dist/query-scenarios.test-support.js.map +1 -0
- package/dist/schemas/cluster-compare.d.ts +11 -0
- package/dist/schemas/cluster-compare.d.ts.map +1 -0
- package/dist/schemas/cluster-compare.js +35 -0
- package/dist/schemas/cluster-compare.js.map +1 -0
- package/dist/schemas/engine-compare.d.ts +10 -0
- package/dist/schemas/engine-compare.d.ts.map +1 -0
- package/dist/schemas/engine-compare.js +27 -0
- package/dist/schemas/engine-compare.js.map +1 -0
- package/dist/schemas/engine-landscape.d.ts +5 -0
- package/dist/schemas/engine-landscape.d.ts.map +1 -0
- package/dist/schemas/engine-landscape.js +8 -0
- package/dist/schemas/engine-landscape.js.map +1 -0
- package/dist/schemas/failures.d.ts +8 -0
- package/dist/schemas/failures.d.ts.map +1 -0
- package/dist/schemas/failures.js +9 -0
- package/dist/schemas/failures.js.map +1 -0
- package/dist/schemas/health.d.ts +5 -0
- package/dist/schemas/health.d.ts.map +1 -0
- package/dist/schemas/health.js +6 -0
- package/dist/schemas/health.js.map +1 -0
- package/dist/schemas/index.d.ts +10 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +10 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/path.d.ts +15 -0
- package/dist/schemas/path.d.ts.map +1 -0
- package/dist/schemas/path.js +21 -0
- package/dist/schemas/path.js.map +1 -0
- package/dist/schemas/performance.d.ts +23 -0
- package/dist/schemas/performance.d.ts.map +1 -0
- package/dist/schemas/performance.js +21 -0
- package/dist/schemas/performance.js.map +1 -0
- package/dist/schemas/shared.d.ts +34 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +34 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/version-compare.d.ts +10 -0
- package/dist/schemas/version-compare.d.ts.map +1 -0
- package/dist/schemas/version-compare.js +25 -0
- package/dist/schemas/version-compare.js.map +1 -0
- package/dist/widgets.d.ts +138 -0
- package/dist/widgets.d.ts.map +1 -0
- package/dist/widgets.js +2 -0
- package/dist/widgets.js.map +1 -0
- package/metrics-contract.json +174 -0
- package/package.json +72 -3
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { GAUGE_LABELS, METRIC_NAMES as M } from "../metric-names.js";
|
|
2
|
+
import { RETENTION_DAYS } from "../prometheus.js";
|
|
3
|
+
import { DAY_SECONDS, nowSeconds, rangeSeconds } from "./windows.js";
|
|
4
|
+
/** Round to one decimal place (KPI seconds / percentages). */
|
|
5
|
+
export const round1 = (n) => Math.round(n * 10) / 10;
|
|
6
|
+
/** Milliseconds (the duration histograms' unit) as seconds, one decimal. */
|
|
7
|
+
export const secondsOf = (ms) => round1(ms / 1000);
|
|
8
|
+
/** {@link secondsOf} that passes `null` (not measured) through. */
|
|
9
|
+
export const secondsOrNull = (ms) => (ms === null ? null : secondsOf(ms));
|
|
10
|
+
/**
|
|
11
|
+
* How long a series must have been silent before a window for it to count as
|
|
12
|
+
* born inside that window. Long enough that a scrape gap at the window's start
|
|
13
|
+
* does not pass an old series off as new (its whole history would then count).
|
|
14
|
+
*/
|
|
15
|
+
const BORN_LOOKBACK = "1h";
|
|
16
|
+
const BORN_LOOKBACK_SECONDS = 3600;
|
|
17
|
+
/**
|
|
18
|
+
* What a counter or histogram series recorded inside `w`, per series — the
|
|
19
|
+
* ONE way every query reads a counter (contract rule 2). The plugin creates a
|
|
20
|
+
* series on its first event, and `increase()` never counts a series' first
|
|
21
|
+
* sample: a rare event such as the first incident of a type at an activity
|
|
22
|
+
* would read 0. So a series that existed before the window counts its
|
|
23
|
+
* `increase()`, and a series born inside it counts its whole value.
|
|
24
|
+
* `series` is a metric name plus its selector, e.g. `camunda_x{engine_id="a"}`.
|
|
25
|
+
*
|
|
26
|
+
* The check looks at the hour before the window. Where that hour lies past
|
|
27
|
+
* the retention (the 30-day period), Prometheus has deleted it and every
|
|
28
|
+
* series would pass for new, counting its whole history; there it looks at
|
|
29
|
+
* the window's own first hour instead: a series silent then was born later.
|
|
30
|
+
* Only a window of an hour or less at the retention edge has no hour to look
|
|
31
|
+
* at and reads plain `increase()`, which can miss a series' first sample but
|
|
32
|
+
* never counts what happened before the window.
|
|
33
|
+
*/
|
|
34
|
+
export function increaseIn(series, w) {
|
|
35
|
+
const at = w.at === undefined ? "" : ` @ ${w.at}`;
|
|
36
|
+
const plain = `increase(${series}[${w.range}]${at})`;
|
|
37
|
+
const offset = bornCheckOffset(w);
|
|
38
|
+
if (offset === null)
|
|
39
|
+
return plain;
|
|
40
|
+
const silent = `present_over_time(${series}[${BORN_LOOKBACK}]${at} offset ${offset})`;
|
|
41
|
+
return (`((${plain} and ${silent})` +
|
|
42
|
+
` or (max_over_time(${series}[${w.range}]${at}) unless ${silent}))`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* How far back from the window's end the born check's hour ends: the window's
|
|
46
|
+
* start (the hour before it), or, where that hour lies past the retention,
|
|
47
|
+
* the end of the window's first hour; `null` when the window has none.
|
|
48
|
+
*/
|
|
49
|
+
function bornCheckOffset(w, now = nowSeconds()) {
|
|
50
|
+
const seconds = rangeSeconds(w.range);
|
|
51
|
+
const start = (w.at ?? now) - seconds;
|
|
52
|
+
if (start - BORN_LOOKBACK_SECONDS >= now - RETENTION_DAYS * DAY_SECONDS)
|
|
53
|
+
return w.range;
|
|
54
|
+
return seconds > BORN_LOOKBACK_SECONDS ? `${seconds - BORN_LOOKBACK_SECONDS}s` : null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* An engine-state gauge with one row per engine-wide figure (contract rule 3):
|
|
58
|
+
* every node of a clustered engine reports the same rows, so the nodes are
|
|
59
|
+
* folded with `max` over the gauge's contract labels before anything sums them.
|
|
60
|
+
*/
|
|
61
|
+
export const engineState = (gauge, sel) => `max by (${GAUGE_LABELS[gauge].join(", ")})(${gauge}${sel})`;
|
|
62
|
+
/**
|
|
63
|
+
* Parse an ISO datetime into epoch seconds. Throws a caller-readable error
|
|
64
|
+
* instead of letting a NaN reach PromQL as `[NaNs] @ NaN` (Prometheus would
|
|
65
|
+
* answer with an opaque parse error).
|
|
66
|
+
*/
|
|
67
|
+
export function parseIsoSeconds(value, field) {
|
|
68
|
+
const ms = Date.parse(value);
|
|
69
|
+
if (!Number.isFinite(ms)) {
|
|
70
|
+
throw new Error(`${field} "${value}" is not a parseable ISO datetime (expected e.g. 2026-07-01T12:00:00Z)`);
|
|
71
|
+
}
|
|
72
|
+
return Math.round(ms / 1000);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Value of the first sample, or 0 when the query returned no series — for
|
|
76
|
+
* COUNTS (`sum(increase(…))`), where "no series in the window" means nothing
|
|
77
|
+
* happened. Never use it for a duration or a gauge: there a missing series is
|
|
78
|
+
* "not measured", see {@link firstOrNull}.
|
|
79
|
+
*/
|
|
80
|
+
export const first = (s) => (s.length ? s[0].value : 0);
|
|
81
|
+
/**
|
|
82
|
+
* Value of the first sample, or `null` when the query returned none — for
|
|
83
|
+
* durations and live gauges. A duration over an empty window is NaN
|
|
84
|
+
* (`0 / 0`, `histogram_quantile` over all-zero buckets), which the client
|
|
85
|
+
* drops, so "nothing ended in the window" lands here as `null`, never as a
|
|
86
|
+
* plausible 0 s. A per-key gauge without a series is 0 while the engines
|
|
87
|
+
* report and "not reported" only while they do not — decide that with
|
|
88
|
+
* {@link reportingEnginesQuery}, never from the gauge's own series.
|
|
89
|
+
*/
|
|
90
|
+
export const firstOrNull = (s) => (s.length ? s[0].value : null);
|
|
91
|
+
/**
|
|
92
|
+
* The engine-state presence probe — one series per engine in scope that
|
|
93
|
+
* reports its state gauges. `camunda_jobs_executable` carries `engine_id`
|
|
94
|
+
* only and is registered on every tick, 0 included; the per-key gauges are
|
|
95
|
+
* not: the metrics plugin registers a row only for what exists (no incident
|
|
96
|
+
* open → no `camunda_incidents_open` series at all). So a per-key gauge
|
|
97
|
+
* without a series is a measured 0 while this probe answers, and "not
|
|
98
|
+
* reported" only while it does not. `engineSel` must hold the engine matcher
|
|
99
|
+
* alone — the gauge has no other label to match.
|
|
100
|
+
*/
|
|
101
|
+
export const reportingEnginesQuery = (engineSel) => engineState(M.jobsExecutable, engineSel);
|
|
102
|
+
/** The engine ids the {@link reportingEnginesQuery} probe answered for, ascending. */
|
|
103
|
+
export const reportingEngineIds = (samples) => [...new Set(samples.flatMap((s) => (s.metric.engine_id ? [s.metric.engine_id] : [])))].sort();
|
|
104
|
+
/** The as-of stamp of a result: now, as an ISO timestamp (the instant queries evaluate at now). */
|
|
105
|
+
export const asOfNow = () => new Date().toISOString();
|
|
106
|
+
/**
|
|
107
|
+
* Index sample values by one label, dropping samples that miss the label.
|
|
108
|
+
* The last sample wins on duplicate label values.
|
|
109
|
+
*/
|
|
110
|
+
export function byLabel(samples, label) {
|
|
111
|
+
const out = {};
|
|
112
|
+
for (const s of samples) {
|
|
113
|
+
const k = s.metric[label];
|
|
114
|
+
if (k !== undefined)
|
|
115
|
+
out[k] = s.value;
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* `part` per 100 of `base`, one decimal — `null` when `base` is 0 (a rate of
|
|
121
|
+
* nothing is undefined, not 0 %). Incident rates divide incidents by
|
|
122
|
+
* instances; one instance can carry several incidents, so they may exceed 100.
|
|
123
|
+
*/
|
|
124
|
+
export function ratePct(part, base) {
|
|
125
|
+
return base > 0 ? round1((part * 100) / base) : null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Percentage change from `before` to `after`, rounded to two decimals. `null`
|
|
129
|
+
* when either side is not measured or the baseline is 0 — never a −100 % from
|
|
130
|
+
* a missing value or a division by zero.
|
|
131
|
+
*/
|
|
132
|
+
export function pctChange(before, after) {
|
|
133
|
+
if (before === null || after === null || before === 0)
|
|
134
|
+
return null;
|
|
135
|
+
return Math.round(((after - before) / before) * 10000) / 100;
|
|
136
|
+
}
|
|
137
|
+
/** Percentage-point difference of two rates; `null` when either is not measured. */
|
|
138
|
+
export function ppDelta(before, after) {
|
|
139
|
+
return before === null || after === null ? null : round1(after - before);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The shared instance-KPI PromQL block used by the dashboard, performance and
|
|
143
|
+
* compare modules: counts via {@link increaseIn} over the window, durations
|
|
144
|
+
* from the histogram (`histogram_quantile` for p50/p95, sum/count for the
|
|
145
|
+
* mean), in MILLISECONDS — the histogram's unit; callers convert. Callers pick
|
|
146
|
+
* the strings they need.
|
|
147
|
+
*
|
|
148
|
+
* The duration histogram is recorded when an instance ENDS (any end state), so
|
|
149
|
+
* every duration covers the instances that ended inside the window only —
|
|
150
|
+
* still-running long instances are not in it (survivorship), and an empty
|
|
151
|
+
* window yields no value at all.
|
|
152
|
+
*/
|
|
153
|
+
export function kpiQueries(scope, w) {
|
|
154
|
+
const { sel, completedSel } = scope;
|
|
155
|
+
const duration = M.processInstanceDuration;
|
|
156
|
+
return {
|
|
157
|
+
started: `sum(${increaseIn(`${M.processInstanceStarted}${sel}`, w)})`,
|
|
158
|
+
completed: `sum(${increaseIn(`${M.processInstanceEnded}${completedSel}`, w)})`,
|
|
159
|
+
incidents: `sum(${increaseIn(`${M.incidentCreated}${sel}`, w)})`,
|
|
160
|
+
avgDuration: `sum(${increaseIn(`${duration}_sum${sel}`, w)}) / sum(${increaseIn(`${duration}_count${sel}`, w)})`,
|
|
161
|
+
medianDuration: `histogram_quantile(0.5, sum by (le)(${increaseIn(`${duration}_bucket${sel}`, w)}))`,
|
|
162
|
+
p95Duration: `histogram_quantile(0.95, sum by (le)(${increaseIn(`${duration}_bucket${sel}`, w)}))`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Runs the KPI block shared by the compare modules (cluster / engine /
|
|
167
|
+
* version) for one partition: five queries, plus the element-scoped incident
|
|
168
|
+
* count when `scope.elementIncidentSel` is set.
|
|
169
|
+
*/
|
|
170
|
+
export async function queryCompareKpis(ch, scope, w) {
|
|
171
|
+
const q = kpiQueries(scope, w);
|
|
172
|
+
const element = scope.elementIncidentSel;
|
|
173
|
+
const [total, completed, incidents, avg, p95, elementIncidents] = await Promise.all([
|
|
174
|
+
ch.instant(q.started),
|
|
175
|
+
ch.instant(q.completed),
|
|
176
|
+
ch.instant(q.incidents),
|
|
177
|
+
ch.instant(q.avgDuration),
|
|
178
|
+
ch.instant(q.p95Duration),
|
|
179
|
+
element === undefined
|
|
180
|
+
? Promise.resolve(null)
|
|
181
|
+
: ch.instant(`sum(${increaseIn(`${M.incidentCreated}${element}`, w)})`),
|
|
182
|
+
]);
|
|
183
|
+
const instances = Math.round(first(total));
|
|
184
|
+
const incidentCount = Math.round(first(incidents));
|
|
185
|
+
const elementCount = elementIncidents === null ? null : Math.round(first(elementIncidents));
|
|
186
|
+
return {
|
|
187
|
+
instance_count: instances,
|
|
188
|
+
completed_count: Math.round(first(completed)),
|
|
189
|
+
incident_count: incidentCount,
|
|
190
|
+
incident_rate_pct: ratePct(incidentCount, instances),
|
|
191
|
+
element_incident_count: elementCount,
|
|
192
|
+
element_incident_rate_pct: elementCount === null ? null : ratePct(elementCount, instances),
|
|
193
|
+
avg_duration_sec: secondsOrNull(firstOrNull(avg)),
|
|
194
|
+
p95_duration_sec: secondsOrNull(firstOrNull(p95)),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Shared delta block of the compare modules. `days` are the two windows'
|
|
199
|
+
* actual lengths; the start counts are compared per day.
|
|
200
|
+
*/
|
|
201
|
+
export function compareKpiDelta(baseline, other, days) {
|
|
202
|
+
const perDay = (count, d) => (d > 0 ? count / d : null);
|
|
203
|
+
return {
|
|
204
|
+
started_per_day_delta_pct: pctChange(perDay(baseline.instance_count, days.baseline), perDay(other.instance_count, days.other)),
|
|
205
|
+
incident_rate_delta_pp: ppDelta(baseline.incident_rate_pct, other.incident_rate_pct),
|
|
206
|
+
element_incident_rate_delta_pp: ppDelta(baseline.element_incident_rate_pct, other.element_incident_rate_pct),
|
|
207
|
+
avg_duration_delta_pct: pctChange(baseline.avg_duration_sec, other.avg_duration_sec),
|
|
208
|
+
p95_duration_delta_pct: pctChange(baseline.p95_duration_sec, other.p95_duration_sec),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Whether a comparison is below the trust threshold: a partition with fewer
|
|
213
|
+
* than `minBucketSize` started instances (the rates' base) OR fewer completed
|
|
214
|
+
* ones (the durations' sample) is noise, not a signal.
|
|
215
|
+
*/
|
|
216
|
+
export function belowMinBucket(partitions, minBucketSize) {
|
|
217
|
+
return partitions.some((p) => p.instance_count < minBucketSize || p.completed_count < minBucketSize);
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/queries/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.test.d.ts","sourceRoot":"","sources":["../../src/queries/helpers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { increaseIn } from "./helpers.js";
|
|
3
|
+
import { DAY_SECONDS, rangeSeconds } from "./windows.js";
|
|
4
|
+
const NOW = Date.UTC(2026, 9, 12, 12, 0, 0);
|
|
5
|
+
const NOW_SECONDS = NOW / 1000;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.useFakeTimers({ toFake: ["Date"], now: NOW });
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
describe("increaseIn", () => {
|
|
13
|
+
it("counts a series born inside the window with its whole value", () => {
|
|
14
|
+
expect(increaseIn('camunda_x_total{engine_id="a"}', { range: "7d" })).toBe('((increase(camunda_x_total{engine_id="a"}[7d]) and present_over_time(camunda_x_total{engine_id="a"}[1h] offset 7d))' +
|
|
15
|
+
' or (max_over_time(camunda_x_total{engine_id="a"}[7d]) unless present_over_time(camunda_x_total{engine_id="a"}[1h] offset 7d)))');
|
|
16
|
+
});
|
|
17
|
+
it("pins every part of a window that ends in the past", () => {
|
|
18
|
+
const at = NOW_SECONDS - 3 * DAY_SECONDS;
|
|
19
|
+
expect(increaseIn("camunda_x_total", { range: "86400s", at })).toBe(`((increase(camunda_x_total[86400s] @ ${at}) and present_over_time(camunda_x_total[1h] @ ${at} offset 86400s))` +
|
|
20
|
+
` or (max_over_time(camunda_x_total[86400s] @ ${at}) unless present_over_time(camunda_x_total[1h] @ ${at} offset 86400s)))`);
|
|
21
|
+
});
|
|
22
|
+
it("checks the window's first hour where the hour before it lies past the retention", () => {
|
|
23
|
+
// 30 days back reaches the retention edge: the series silent in the window's
|
|
24
|
+
// first hour (30d − 1h = 2588400s before its end) were born inside it.
|
|
25
|
+
expect(increaseIn("camunda_x_total", { range: "30d" })).toBe("((increase(camunda_x_total[30d]) and present_over_time(camunda_x_total[1h] offset 2588400s))" +
|
|
26
|
+
" or (max_over_time(camunda_x_total[30d]) unless present_over_time(camunda_x_total[1h] offset 2588400s)))");
|
|
27
|
+
const at = NOW_SECONDS - 29 * DAY_SECONDS;
|
|
28
|
+
expect(increaseIn("camunda_x_total", { range: "1d", at })).toContain(`present_over_time(camunda_x_total[1h] @ ${at} offset 82800s)`);
|
|
29
|
+
// The hour before the window starts exactly at the retention edge: still that hour.
|
|
30
|
+
const edge = NOW_SECONDS - 30 * DAY_SECONDS + 3600 + DAY_SECONDS;
|
|
31
|
+
expect(increaseIn("camunda_x_total", { range: "1d", at: edge })).toContain(`present_over_time(camunda_x_total[1h] @ ${edge} offset 1d)`);
|
|
32
|
+
expect(increaseIn("camunda_x_total", { range: "1d", at: edge - 1 })).toContain(`present_over_time(camunda_x_total[1h] @ ${edge - 1} offset 82800s)`);
|
|
33
|
+
});
|
|
34
|
+
it("reads plain increase() for a window of an hour or less at the retention edge", () => {
|
|
35
|
+
const at = NOW_SECONDS - 30 * DAY_SECONDS + 1800;
|
|
36
|
+
expect(increaseIn("camunda_x_total", { range: "3600s", at })).toBe(`increase(camunda_x_total[3600s] @ ${at})`);
|
|
37
|
+
});
|
|
38
|
+
it("refuses a range it cannot measure", () => {
|
|
39
|
+
expect(() => increaseIn("camunda_x_total", { range: "1d12h" })).toThrow(/single-unit/);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("rangeSeconds", () => {
|
|
43
|
+
it("measures every single-unit PromQL duration", () => {
|
|
44
|
+
expect(["30s", "5m", "2h", "1d", "1w"].map(rangeSeconds)).toEqual([
|
|
45
|
+
30,
|
|
46
|
+
300,
|
|
47
|
+
7200,
|
|
48
|
+
DAY_SECONDS,
|
|
49
|
+
7 * DAY_SECONDS,
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=helpers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.test.js","sourceRoot":"","sources":["../../src/queries/helpers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type { CompareKpis, CompareKpiDelta } from "./helpers.js";
|
|
2
|
+
export { analyzePerformance, comparePeriods } from "./performance.js";
|
|
3
|
+
export type { PerformanceResult, PerformanceKPI, ActivityBreakdownRow, PeriodComparisonKpi, PeriodActivityComparisonRow, PeriodComparisonResult, } from "./performance.js";
|
|
4
|
+
export { findFailedInstances } from "./failures.js";
|
|
5
|
+
export type { ErrorPatternRow, FailedInstancesResult } from "./failures.js";
|
|
6
|
+
export { elementBottleneck, elementHeat } from "./element.js";
|
|
7
|
+
export type { ElementBottleneckRow, ElementBottleneckResult, ElementHeatResult } from "./element.js";
|
|
8
|
+
export { clusterCompare } from "./cluster-compare.js";
|
|
9
|
+
export type { ClusterCompareResult, ClusterCompareKpi, ClusterCompareDelta, } from "./cluster-compare.js";
|
|
10
|
+
export { versionCompare } from "./version-compare.js";
|
|
11
|
+
export type { VersionCompareResult, VersionCompareKpi, VersionCompareDelta, } from "./version-compare.js";
|
|
12
|
+
export { engineCompare } from "./engine-compare.js";
|
|
13
|
+
export type { EngineCompareResult, EngineCompareKpi, EngineCompareDelta } from "./engine-compare.js";
|
|
14
|
+
export { engineLandscape } from "./engine-landscape.js";
|
|
15
|
+
export type { EngineLandscapeResult, EngineLandscapeEngine, EngineLandscapeProcess, } from "./engine-landscape.js";
|
|
16
|
+
export { dashboardData, failureDashboardData } from "./dashboard.js";
|
|
17
|
+
export { engineHealth } from "./health.js";
|
|
18
|
+
export type { EngineHealthResult, HealthCount, HealthAlert } from "./health.js";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/queries/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrE,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC7D,YAAY,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { analyzePerformance, comparePeriods } from "./performance.js";
|
|
2
|
+
export { findFailedInstances } from "./failures.js";
|
|
3
|
+
export { elementBottleneck, elementHeat } from "./element.js";
|
|
4
|
+
export { clusterCompare } from "./cluster-compare.js";
|
|
5
|
+
export { versionCompare } from "./version-compare.js";
|
|
6
|
+
export { engineCompare } from "./engine-compare.js";
|
|
7
|
+
export { engineLandscape } from "./engine-landscape.js";
|
|
8
|
+
export { dashboardData, failureDashboardData } from "./dashboard.js";
|
|
9
|
+
export { engineHealth } from "./health.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/queries/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AASrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAEnD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAMrD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAMrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAMvD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type EngineFilterInput, type PrometheusClient } from "../prometheus.js";
|
|
2
|
+
export interface PerformanceKPI {
|
|
3
|
+
process_definition_key: string;
|
|
4
|
+
/** Instances started in the window. */
|
|
5
|
+
total_instances: number;
|
|
6
|
+
/** Instances that ended with state COMPLETED in the window. */
|
|
7
|
+
completed: number;
|
|
8
|
+
/** Incidents created in the window — incidents, not failed instances. */
|
|
9
|
+
incident_count: number;
|
|
10
|
+
/** `incident_count` per 100 started instances (may exceed 100); null when nothing started. */
|
|
11
|
+
incident_rate_pct: number | null;
|
|
12
|
+
/** Durations of the instances that ENDED in the window; null when none did. */
|
|
13
|
+
avg_duration_sec: number | null;
|
|
14
|
+
median_duration_sec: number | null;
|
|
15
|
+
p95_duration_sec: number | null;
|
|
16
|
+
}
|
|
17
|
+
export interface ActivityBreakdownRow {
|
|
18
|
+
activity_id: string;
|
|
19
|
+
/** Not a metric label — resolve names from the BPMN. Always null. */
|
|
20
|
+
activity_name: null;
|
|
21
|
+
activity_type: string;
|
|
22
|
+
execution_count: number;
|
|
23
|
+
avg_duration_sec: number | null;
|
|
24
|
+
median_duration_sec: number | null;
|
|
25
|
+
p95_duration_sec: number | null;
|
|
26
|
+
total_time_sec: number;
|
|
27
|
+
}
|
|
28
|
+
export interface PeriodComparisonKpi {
|
|
29
|
+
period: string;
|
|
30
|
+
/** The window actually measured, after clamping to now and retention. */
|
|
31
|
+
window_from: string;
|
|
32
|
+
window_to: string;
|
|
33
|
+
window_days: number;
|
|
34
|
+
/** True when clamping cut the requested window short. */
|
|
35
|
+
partial: boolean;
|
|
36
|
+
total_instances: number;
|
|
37
|
+
/**
|
|
38
|
+
* `total_instances` per day of the window's exact length — the figure to
|
|
39
|
+
* compare across windows of different length. Always measured: a clamped
|
|
40
|
+
* window is never empty (`clampWindow` refuses one).
|
|
41
|
+
*/
|
|
42
|
+
started_per_day: number;
|
|
43
|
+
completed: number;
|
|
44
|
+
incident_count: number;
|
|
45
|
+
incident_rate_pct: number | null;
|
|
46
|
+
avg_duration_sec: number | null;
|
|
47
|
+
median_sec: number | null;
|
|
48
|
+
p95_sec: number | null;
|
|
49
|
+
}
|
|
50
|
+
export interface PeriodActivityComparisonRow {
|
|
51
|
+
activity_id: string;
|
|
52
|
+
activity_name: null;
|
|
53
|
+
period: string;
|
|
54
|
+
executions: number;
|
|
55
|
+
avg_sec: number | null;
|
|
56
|
+
p95_sec: number | null;
|
|
57
|
+
}
|
|
58
|
+
/** Result of {@link analyzePerformance}. */
|
|
59
|
+
export interface PerformanceResult {
|
|
60
|
+
/** The engine ids covered; `null` = every engine Prometheus holds (unscoped library call). */
|
|
61
|
+
engines: string[] | null;
|
|
62
|
+
kpi: PerformanceKPI | null;
|
|
63
|
+
activityBreakdown: ActivityBreakdownRow[];
|
|
64
|
+
}
|
|
65
|
+
export interface PeriodComparisonResult {
|
|
66
|
+
/** The engine ids covered; `null` = every engine Prometheus holds (unscoped library call). */
|
|
67
|
+
engines: string[] | null;
|
|
68
|
+
kpiComparison: PeriodComparisonKpi[];
|
|
69
|
+
activityComparison?: PeriodActivityComparisonRow[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Process performance KPIs over a rolling window, from OTEL metrics.
|
|
73
|
+
*
|
|
74
|
+
* Counts come from {@link increaseIn} over the period; durations from the histogram
|
|
75
|
+
* (`histogram_quantile` for p50/p95, sum/count for the mean) and cover the
|
|
76
|
+
* instances that ENDED in the window — `null` when none did. `incident_count`
|
|
77
|
+
* counts incidents created (metrics carry no per-instance terminal-failure
|
|
78
|
+
* state). `kpi` is null only when the window saw no start, end or incident.
|
|
79
|
+
*/
|
|
80
|
+
export declare function analyzePerformance(ch: PrometheusClient, params: {
|
|
81
|
+
processDefinitionKey: string;
|
|
82
|
+
period: string;
|
|
83
|
+
includeActivityBreakdown: boolean;
|
|
84
|
+
engine?: EngineFilterInput;
|
|
85
|
+
}): Promise<PerformanceResult>;
|
|
86
|
+
/**
|
|
87
|
+
* Compare two explicit execution windows. Each is read as a real historical
|
|
88
|
+
* window via the PromQL `@ <end>` modifier, clamped to `[now − retention,
|
|
89
|
+
* now]` and reported with its ACTUAL bounds (`window_*`, `partial`). Windows
|
|
90
|
+
* of different length are not comparable by count, so each KPI row also
|
|
91
|
+
* carries `started_per_day`. A reversed window, or one entirely in the future
|
|
92
|
+
* or before retention, is refused.
|
|
93
|
+
*/
|
|
94
|
+
export declare function comparePeriods(ch: PrometheusClient, params: {
|
|
95
|
+
processDefinitionKey: string;
|
|
96
|
+
periodAFrom: string;
|
|
97
|
+
periodATo: string;
|
|
98
|
+
periodBFrom: string;
|
|
99
|
+
periodBTo: string;
|
|
100
|
+
includeActivityBreakdown: boolean;
|
|
101
|
+
engine?: EngineFilterInput;
|
|
102
|
+
}): Promise<PeriodComparisonResult>;
|
|
103
|
+
//# sourceMappingURL=performance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/queries/performance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { engineIdsOf, engineMatcher, escapeLabelValue, selector, } from "../prometheus.js";
|
|
2
|
+
import { METRIC_NAMES as M } from "../metric-names.js";
|
|
3
|
+
import { byLabel, first, firstOrNull, increaseIn, kpiQueries, parseIsoSeconds, ratePct, round1, secondsOf, secondsOrNull, } from "./helpers.js";
|
|
4
|
+
import { clampWindow, DAY_SECONDS, daysOf, isoOf, nowSeconds, windowAt, } from "./windows.js";
|
|
5
|
+
/** `{process_definition_key=..., engine_id=...}` plus any extra matchers. */
|
|
6
|
+
function pdkSelector(key, engineId, ...extra) {
|
|
7
|
+
return selector(`process_definition_key="${escapeLabelValue(key)}"`, engineMatcher(engineId), ...extra);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Process performance KPIs over a rolling window, from OTEL metrics.
|
|
11
|
+
*
|
|
12
|
+
* Counts come from {@link increaseIn} over the period; durations from the histogram
|
|
13
|
+
* (`histogram_quantile` for p50/p95, sum/count for the mean) and cover the
|
|
14
|
+
* instances that ENDED in the window — `null` when none did. `incident_count`
|
|
15
|
+
* counts incidents created (metrics carry no per-instance terminal-failure
|
|
16
|
+
* state). `kpi` is null only when the window saw no start, end or incident.
|
|
17
|
+
*/
|
|
18
|
+
export async function analyzePerformance(ch, params) {
|
|
19
|
+
const range = params.period ?? "7d";
|
|
20
|
+
const q = kpiQueries({
|
|
21
|
+
sel: pdkSelector(params.processDefinitionKey, params.engine),
|
|
22
|
+
completedSel: pdkSelector(params.processDefinitionKey, params.engine, 'state="COMPLETED"'),
|
|
23
|
+
}, { range });
|
|
24
|
+
const [total, completed, incidents, avg, median, p95] = await Promise.all([
|
|
25
|
+
ch.instant(q.started),
|
|
26
|
+
ch.instant(q.completed),
|
|
27
|
+
ch.instant(q.incidents),
|
|
28
|
+
ch.instant(q.avgDuration),
|
|
29
|
+
ch.instant(q.medianDuration),
|
|
30
|
+
ch.instant(q.p95Duration),
|
|
31
|
+
]);
|
|
32
|
+
const totalInstances = Math.round(first(total));
|
|
33
|
+
const completedCount = Math.round(first(completed));
|
|
34
|
+
const incidentCount = Math.round(first(incidents));
|
|
35
|
+
const avgMs = firstOrNull(avg);
|
|
36
|
+
const observed = totalInstances > 0 || completedCount > 0 || incidentCount > 0 || avgMs !== null;
|
|
37
|
+
const kpi = observed
|
|
38
|
+
? {
|
|
39
|
+
process_definition_key: params.processDefinitionKey,
|
|
40
|
+
total_instances: totalInstances,
|
|
41
|
+
completed: completedCount,
|
|
42
|
+
incident_count: incidentCount,
|
|
43
|
+
incident_rate_pct: ratePct(incidentCount, totalInstances),
|
|
44
|
+
avg_duration_sec: secondsOrNull(avgMs),
|
|
45
|
+
median_duration_sec: secondsOrNull(firstOrNull(median)),
|
|
46
|
+
p95_duration_sec: secondsOrNull(firstOrNull(p95)),
|
|
47
|
+
}
|
|
48
|
+
: null;
|
|
49
|
+
let activityBreakdown = [];
|
|
50
|
+
if (params.includeActivityBreakdown) {
|
|
51
|
+
activityBreakdown = await activityBreakdownRows(ch, params.processDefinitionKey, { range }, params.engine);
|
|
52
|
+
}
|
|
53
|
+
return { engines: engineIdsOf(params.engine), kpi, activityBreakdown };
|
|
54
|
+
}
|
|
55
|
+
async function activityBreakdownRows(ch, key, w, engineId) {
|
|
56
|
+
const sel = pdkSelector(key, engineId);
|
|
57
|
+
const inWindow = (series) => increaseIn(series, w);
|
|
58
|
+
const [counts, sums, median, p95] = await Promise.all([
|
|
59
|
+
ch.instant(`sum by (activity_id, activity_type)(${inWindow(`${M.activityEnded}${sel}`)})`),
|
|
60
|
+
ch.instant(`sum by (activity_id)(${inWindow(`${M.activityDuration}_sum${sel}`)})`),
|
|
61
|
+
ch.instant(`histogram_quantile(0.5, sum by (activity_id, le)(${inWindow(`${M.activityDuration}_bucket${sel}`)}))`),
|
|
62
|
+
ch.instant(`histogram_quantile(0.95, sum by (activity_id, le)(${inWindow(`${M.activityDuration}_bucket${sel}`)}))`),
|
|
63
|
+
]);
|
|
64
|
+
const sumBy = byLabel(sums, "activity_id");
|
|
65
|
+
const medianBy = byLabel(median, "activity_id");
|
|
66
|
+
const p95By = byLabel(p95, "activity_id");
|
|
67
|
+
const rows = counts.map((c) => {
|
|
68
|
+
const id = c.metric.activity_id ?? "";
|
|
69
|
+
const count = Math.round(c.value);
|
|
70
|
+
const sumMs = sumBy[id];
|
|
71
|
+
return {
|
|
72
|
+
activity_id: id,
|
|
73
|
+
activity_name: null,
|
|
74
|
+
activity_type: c.metric.activity_type ?? "",
|
|
75
|
+
execution_count: count,
|
|
76
|
+
avg_duration_sec: sumMs !== undefined && count > 0 ? secondsOf(sumMs / count) : null,
|
|
77
|
+
median_duration_sec: secondsOrNull(medianBy[id] ?? null),
|
|
78
|
+
p95_duration_sec: secondsOrNull(p95By[id] ?? null),
|
|
79
|
+
total_time_sec: secondsOf(sumMs ?? 0),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
return rows.sort((a, b) => b.total_time_sec - a.total_time_sec).slice(0, 20);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Compare two explicit execution windows. Each is read as a real historical
|
|
86
|
+
* window via the PromQL `@ <end>` modifier, clamped to `[now − retention,
|
|
87
|
+
* now]` and reported with its ACTUAL bounds (`window_*`, `partial`). Windows
|
|
88
|
+
* of different length are not comparable by count, so each KPI row also
|
|
89
|
+
* carries `started_per_day`. A reversed window, or one entirely in the future
|
|
90
|
+
* or before retention, is refused.
|
|
91
|
+
*/
|
|
92
|
+
export async function comparePeriods(ch, params) {
|
|
93
|
+
const now = nowSeconds();
|
|
94
|
+
const a = explicitWindow("periodA", params.periodAFrom, params.periodATo, now);
|
|
95
|
+
const b = explicitWindow("periodB", params.periodBFrom, params.periodBTo, now);
|
|
96
|
+
const [kpiA, kpiB] = await Promise.all([
|
|
97
|
+
periodKpi(ch, params.processDefinitionKey, "Period A", a, params.engine),
|
|
98
|
+
periodKpi(ch, params.processDefinitionKey, "Period B", b, params.engine),
|
|
99
|
+
]);
|
|
100
|
+
const result = {
|
|
101
|
+
engines: engineIdsOf(params.engine),
|
|
102
|
+
kpiComparison: [kpiA, kpiB],
|
|
103
|
+
};
|
|
104
|
+
if (params.includeActivityBreakdown) {
|
|
105
|
+
const [actA, actB] = await Promise.all([
|
|
106
|
+
periodActivities(ch, params.processDefinitionKey, "Period A", a, params.engine),
|
|
107
|
+
periodActivities(ch, params.processDefinitionKey, "Period B", b, params.engine),
|
|
108
|
+
]);
|
|
109
|
+
result.activityComparison = [...actA, ...actB].sort((x, y) => x.activity_id.localeCompare(y.activity_id) || x.period.localeCompare(y.period));
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
function explicitWindow(name, from, to, now) {
|
|
114
|
+
return clampWindow(name, parseIsoSeconds(from, `${name}From`), parseIsoSeconds(to, `${name}To`), now);
|
|
115
|
+
}
|
|
116
|
+
async function periodKpi(ch, key, label, w, engineId) {
|
|
117
|
+
const q = kpiQueries({
|
|
118
|
+
sel: pdkSelector(key, engineId),
|
|
119
|
+
completedSel: pdkSelector(key, engineId, 'state="COMPLETED"'),
|
|
120
|
+
}, windowAt(w));
|
|
121
|
+
const [total, completed, incidents, avg, median, p95] = await Promise.all([
|
|
122
|
+
ch.instant(q.started),
|
|
123
|
+
ch.instant(q.completed),
|
|
124
|
+
ch.instant(q.incidents),
|
|
125
|
+
ch.instant(q.avgDuration),
|
|
126
|
+
ch.instant(q.medianDuration),
|
|
127
|
+
ch.instant(q.p95Duration),
|
|
128
|
+
]);
|
|
129
|
+
const totalInstances = Math.round(first(total));
|
|
130
|
+
const incidentCount = Math.round(first(incidents));
|
|
131
|
+
return {
|
|
132
|
+
period: label,
|
|
133
|
+
window_from: isoOf(w.from),
|
|
134
|
+
window_to: isoOf(w.to),
|
|
135
|
+
window_days: daysOf(w.seconds),
|
|
136
|
+
partial: w.partial,
|
|
137
|
+
total_instances: totalInstances,
|
|
138
|
+
// From the exact length — the rounded window_days is for display: a
|
|
139
|
+
// 10-minute window is 0.01 d there (−30 %), a 5-minute one 0.00 d.
|
|
140
|
+
started_per_day: round1((totalInstances * DAY_SECONDS) / w.seconds),
|
|
141
|
+
completed: Math.round(first(completed)),
|
|
142
|
+
incident_count: incidentCount,
|
|
143
|
+
incident_rate_pct: ratePct(incidentCount, totalInstances),
|
|
144
|
+
avg_duration_sec: secondsOrNull(firstOrNull(avg)),
|
|
145
|
+
median_sec: secondsOrNull(firstOrNull(median)),
|
|
146
|
+
p95_sec: secondsOrNull(firstOrNull(p95)),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async function periodActivities(ch, key, label, w, engineId) {
|
|
150
|
+
const sel = pdkSelector(key, engineId);
|
|
151
|
+
const inWindow = (series) => increaseIn(series, windowAt(w));
|
|
152
|
+
const [counts, sums, p95] = await Promise.all([
|
|
153
|
+
ch.instant(`sum by (activity_id)(${inWindow(`${M.activityEnded}${sel}`)})`),
|
|
154
|
+
ch.instant(`sum by (activity_id)(${inWindow(`${M.activityDuration}_sum${sel}`)})`),
|
|
155
|
+
ch.instant(`histogram_quantile(0.95, sum by (activity_id, le)(${inWindow(`${M.activityDuration}_bucket${sel}`)}))`),
|
|
156
|
+
]);
|
|
157
|
+
const sumBy = byLabel(sums, "activity_id");
|
|
158
|
+
const p95By = byLabel(p95, "activity_id");
|
|
159
|
+
return counts.map((c) => {
|
|
160
|
+
const id = c.metric.activity_id ?? "";
|
|
161
|
+
const executions = Math.round(c.value);
|
|
162
|
+
const sumMs = sumBy[id];
|
|
163
|
+
return {
|
|
164
|
+
activity_id: id,
|
|
165
|
+
activity_name: null,
|
|
166
|
+
period: label,
|
|
167
|
+
executions,
|
|
168
|
+
avg_sec: sumMs !== undefined && executions > 0 ? secondsOf(sumMs / executions) : null,
|
|
169
|
+
p95_sec: secondsOrNull(p95By[id] ?? null),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=performance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/queries/performance.ts"],"names":[],"mappings":"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"}
|