pipework 0.67.0 → 0.69.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/CHANGELOG.md +13 -0
- package/REFERENCE.md +112 -0
- package/dist/core/config/namespace.d.ts +2 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +6 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +4 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/data/migrate/internal-definitions.d.ts.map +1 -1
- package/dist/data/migrate/internal-definitions.js +10 -0
- package/dist/data/migrate/internal-definitions.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/alerts/audit.d.ts +3 -0
- package/dist/infra/alerts/audit.d.ts.map +1 -0
- package/dist/infra/alerts/audit.js +25 -0
- package/dist/infra/alerts/audit.js.map +1 -0
- package/dist/infra/alerts/handle.d.ts +95 -0
- package/dist/infra/alerts/handle.d.ts.map +1 -0
- package/dist/infra/alerts/handle.js +301 -0
- package/dist/infra/alerts/handle.js.map +1 -0
- package/dist/infra/alerts/index.d.ts +5 -0
- package/dist/infra/alerts/index.d.ts.map +1 -0
- package/dist/infra/alerts/index.js +2 -0
- package/dist/infra/alerts/index.js.map +1 -0
- package/dist/infra/alerts/namespace.d.ts +23 -0
- package/dist/infra/alerts/namespace.d.ts.map +1 -0
- package/dist/infra/alerts/namespace.js +23 -0
- package/dist/infra/alerts/namespace.js.map +1 -0
- package/dist/infra/alerts/schema.d.ts +123 -0
- package/dist/infra/alerts/schema.d.ts.map +1 -0
- package/dist/infra/alerts/schema.js +55 -0
- package/dist/infra/alerts/schema.js.map +1 -0
- package/dist/infra/alerts/sinks.d.ts +60 -0
- package/dist/infra/alerts/sinks.d.ts.map +1 -0
- package/dist/infra/alerts/sinks.js +50 -0
- package/dist/infra/alerts/sinks.js.map +1 -0
- package/dist/infra/alerts/sources.d.ts +55 -0
- package/dist/infra/alerts/sources.d.ts.map +1 -0
- package/dist/infra/alerts/sources.js +134 -0
- package/dist/infra/alerts/sources.js.map +1 -0
- package/dist/infra/aws/sigv4.d.ts +46 -0
- package/dist/infra/aws/sigv4.d.ts.map +1 -0
- package/dist/infra/aws/sigv4.js +94 -0
- package/dist/infra/aws/sigv4.js.map +1 -0
- package/dist/infra/email/index.d.ts +1 -1
- package/dist/infra/email/index.d.ts.map +1 -1
- package/dist/infra/email/index.js +1 -1
- package/dist/infra/email/index.js.map +1 -1
- package/dist/infra/email/providers.d.ts +1 -1
- package/dist/infra/email/providers.d.ts.map +1 -1
- package/dist/infra/email/providers.js +1 -1
- package/dist/infra/email/providers.js.map +1 -1
- package/dist/infra/email/sns.d.ts +1 -1
- package/dist/infra/email/sns.js +1 -1
- package/dist/infra/storage/backend.d.ts +32 -0
- package/dist/infra/storage/backend.d.ts.map +1 -0
- package/dist/infra/storage/backend.js +2 -0
- package/dist/infra/storage/backend.js.map +1 -0
- package/dist/infra/storage/handle.d.ts +110 -0
- package/dist/infra/storage/handle.d.ts.map +1 -0
- package/dist/infra/storage/handle.js +317 -0
- package/dist/infra/storage/handle.js.map +1 -0
- package/dist/infra/storage/index.d.ts +8 -0
- package/dist/infra/storage/index.d.ts.map +1 -0
- package/dist/infra/storage/index.js +4 -0
- package/dist/infra/storage/index.js.map +1 -0
- package/dist/infra/storage/local.d.ts +16 -0
- package/dist/infra/storage/local.d.ts.map +1 -0
- package/dist/infra/storage/local.js +95 -0
- package/dist/infra/storage/local.js.map +1 -0
- package/dist/infra/storage/namespace.d.ts +13 -0
- package/dist/infra/storage/namespace.d.ts.map +1 -0
- package/dist/infra/storage/namespace.js +13 -0
- package/dist/infra/storage/namespace.js.map +1 -0
- package/dist/infra/storage/s3.d.ts +34 -0
- package/dist/infra/storage/s3.d.ts.map +1 -0
- package/dist/infra/storage/s3.js +220 -0
- package/dist/infra/storage/s3.js.map +1 -0
- package/dist/infra/storage/schema.d.ts +43 -0
- package/dist/infra/storage/schema.d.ts.map +1 -0
- package/dist/infra/storage/schema.js +23 -0
- package/dist/infra/storage/schema.js.map +1 -0
- package/dist/infra/webhook/sign.d.ts +2 -0
- package/dist/infra/webhook/sign.d.ts.map +1 -1
- package/dist/infra/webhook/sign.js +1 -1
- package/dist/infra/webhook/sign.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { sql } from '../../data/db/sql.js';
|
|
2
|
+
import { pipeSystem } from '../../data/db/db.js';
|
|
3
|
+
import { fitting } from '../../request/di/index.js';
|
|
4
|
+
import { getContext } from '../../request/context/store.js';
|
|
5
|
+
import { getBaseLogger } from '../../core/logging/index.js';
|
|
6
|
+
import { __flowStep } from '../../trace/runtime/flow-step.js';
|
|
7
|
+
import { auditAlert } from './audit.js';
|
|
8
|
+
const SEND_TIMEOUT_MS = 10_000;
|
|
9
|
+
const LEASE_MS = 30_000;
|
|
10
|
+
const BATCH = 50;
|
|
11
|
+
const MAX_BATCHES = 20;
|
|
12
|
+
const MAX_BACKOFF_MS = 3_600_000;
|
|
13
|
+
const SEVERITIES = new Set(['info', 'warning', 'error', 'critical']);
|
|
14
|
+
function toAlert(row) {
|
|
15
|
+
const date = (v) => new Date(v);
|
|
16
|
+
const dateOrNull = (v) => (v === null || v === undefined ? null : date(v));
|
|
17
|
+
return {
|
|
18
|
+
key: row['key'],
|
|
19
|
+
status: row['status'],
|
|
20
|
+
severity: row['severity'],
|
|
21
|
+
message: row['message'],
|
|
22
|
+
subject: (row['subject'] ?? null),
|
|
23
|
+
count: Number(row['count']),
|
|
24
|
+
firstSeenAt: date(row['first_seen_at']),
|
|
25
|
+
lastSeenAt: date(row['last_seen_at']),
|
|
26
|
+
lastSentAt: dateOrNull(row['last_sent_at']),
|
|
27
|
+
resolvedAt: dateOrNull(row['resolved_at']),
|
|
28
|
+
traceIds: (row['trace_ids'] ?? []),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function toDeliveryRow(row) {
|
|
32
|
+
return {
|
|
33
|
+
id: row['id'],
|
|
34
|
+
key: row['alert_key'],
|
|
35
|
+
kind: row['kind'],
|
|
36
|
+
sink: row['sink'],
|
|
37
|
+
payload: row['payload'],
|
|
38
|
+
attempts: Number(row['attempts']),
|
|
39
|
+
nextAttemptAt: new Date(row['next_attempt_at']),
|
|
40
|
+
deliveredAt: row['delivered_at'] === null ? null : new Date(row['delivered_at']),
|
|
41
|
+
failedAt: row['failed_at'] === null ? null : new Date(row['failed_at']),
|
|
42
|
+
lastError: (row['last_error'] ?? null),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function pgTextArray(values) {
|
|
46
|
+
return `{${values.map(v => `"${v.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`).join(',')}}`;
|
|
47
|
+
}
|
|
48
|
+
function withTimeout(promise, ms) {
|
|
49
|
+
let timer;
|
|
50
|
+
const timeout = new Promise((_, reject) => {
|
|
51
|
+
timer = setTimeout(() => reject(new Error(`sink did not answer within ${String(ms)} ms`)), ms);
|
|
52
|
+
});
|
|
53
|
+
return Promise.race([promise, timeout]).finally(() => { clearTimeout(timer); });
|
|
54
|
+
}
|
|
55
|
+
export function createAlerts(config) {
|
|
56
|
+
if (config.sinks.length === 0)
|
|
57
|
+
throw new Error('[pipework] alerts.create needs at least one sink.');
|
|
58
|
+
const sinks = new Map();
|
|
59
|
+
for (const s of config.sinks) {
|
|
60
|
+
if (sinks.has(s.name))
|
|
61
|
+
throw new Error(`[pipework] alerts.create: two sinks are named "${s.name}"; give each its own name.`);
|
|
62
|
+
sinks.set(s.name, s);
|
|
63
|
+
}
|
|
64
|
+
const sinkNames = pgTextArray([...sinks.keys()]);
|
|
65
|
+
const jobType = config.jobType ?? 'pipework.alerts';
|
|
66
|
+
const maxAttempts = config.maxAttempts ?? 10;
|
|
67
|
+
const backoffMs = config.backoffMs ?? 5000;
|
|
68
|
+
const defaultResend = config.resendAfterMs ?? null;
|
|
69
|
+
const defaultQuiet = config.resolveAfterQuietMs ?? null;
|
|
70
|
+
const sources = config.sources ?? [];
|
|
71
|
+
const database = config.database;
|
|
72
|
+
const log = getBaseLogger();
|
|
73
|
+
const db = () => pipeSystem(database);
|
|
74
|
+
// The alert and its deliveries move in one statement, so a crash cannot leave one without the other.
|
|
75
|
+
const payloadSql = (kind) => sql `jsonb_build_object(
|
|
76
|
+
'kind', ${kind}, 'key', up.key, 'severity', up.severity, 'message', up.message, 'subject', up.subject,
|
|
77
|
+
'count', up.count, 'firstSeenAt', up.first_seen_at, 'lastSeenAt', up.last_seen_at,
|
|
78
|
+
'resolvedAt', up.resolved_at, 'traceIds', to_jsonb(up.trace_ids))`;
|
|
79
|
+
async function enqueueJob() {
|
|
80
|
+
await config.queue.enqueue(db(), { type: jobType, payload: {}, continueFlow: true });
|
|
81
|
+
}
|
|
82
|
+
// Inside a tick the deliveries are sent by that same tick, so it does not enqueue another.
|
|
83
|
+
async function raise(key, options, enqueue = true) {
|
|
84
|
+
if (key.trim() === '')
|
|
85
|
+
throw new Error('[pipework] alerts.raise needs a non-empty key.');
|
|
86
|
+
if (!SEVERITIES.has(options.severity))
|
|
87
|
+
throw new Error(`[pipework] alerts.raise: severity must be info, warning, error or critical, got "${String(options.severity)}".`);
|
|
88
|
+
const traceId = getContext()?.trace?.id ?? null;
|
|
89
|
+
const traceArray = traceId === null ? '{}' : pgTextArray([traceId]);
|
|
90
|
+
const subject = options.subject === undefined ? null : JSON.stringify(options.subject);
|
|
91
|
+
const rows = await db().execute(sql `
|
|
92
|
+
WITH up AS (
|
|
93
|
+
INSERT INTO pipework_alerts AS a
|
|
94
|
+
(key, status, severity, message, subject, count, first_seen_at, last_seen_at, last_sent_at,
|
|
95
|
+
trace_ids, resend_after_ms, resolve_after_quiet_ms, updated_at)
|
|
96
|
+
SELECT ${key}, 'open', ${options.severity}, ${options.message}, ${subject}::jsonb, 1, c.ts, c.ts, c.ts,
|
|
97
|
+
${traceArray}::text[], ${options.resendAfterMs ?? null}::int, ${options.resolveAfterQuietMs ?? null}::int, c.ts
|
|
98
|
+
FROM (SELECT clock_timestamp() AS ts) c
|
|
99
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
100
|
+
status = 'open',
|
|
101
|
+
severity = EXCLUDED.severity,
|
|
102
|
+
message = EXCLUDED.message,
|
|
103
|
+
subject = EXCLUDED.subject,
|
|
104
|
+
count = CASE WHEN a.status = 'resolved' THEN 1 ELSE a.count + 1 END,
|
|
105
|
+
first_seen_at = CASE WHEN a.status = 'resolved' THEN EXCLUDED.first_seen_at ELSE a.first_seen_at END,
|
|
106
|
+
last_seen_at = EXCLUDED.last_seen_at,
|
|
107
|
+
resolved_at = NULL,
|
|
108
|
+
trace_ids = CASE
|
|
109
|
+
WHEN a.status = 'resolved' THEN EXCLUDED.trace_ids
|
|
110
|
+
WHEN ${traceId}::text IS NULL THEN a.trace_ids
|
|
111
|
+
ELSE (EXCLUDED.trace_ids || array_remove(a.trace_ids, ${traceId}::text))[1:10] END,
|
|
112
|
+
resend_after_ms = COALESCE(EXCLUDED.resend_after_ms, CASE WHEN a.status = 'resolved' THEN NULL ELSE a.resend_after_ms END),
|
|
113
|
+
resolve_after_quiet_ms = COALESCE(EXCLUDED.resolve_after_quiet_ms, CASE WHEN a.status = 'resolved' THEN NULL ELSE a.resolve_after_quiet_ms END),
|
|
114
|
+
last_sent_at = CASE
|
|
115
|
+
WHEN a.status = 'resolved' OR a.last_sent_at IS NULL THEN EXCLUDED.last_sent_at
|
|
116
|
+
WHEN COALESCE(EXCLUDED.resend_after_ms, a.resend_after_ms, ${defaultResend}::int) IS NOT NULL
|
|
117
|
+
AND EXCLUDED.last_sent_at - a.last_sent_at
|
|
118
|
+
>= COALESCE(EXCLUDED.resend_after_ms, a.resend_after_ms, ${defaultResend}::int) * interval '1 millisecond'
|
|
119
|
+
THEN EXCLUDED.last_sent_at
|
|
120
|
+
ELSE a.last_sent_at END,
|
|
121
|
+
updated_at = EXCLUDED.updated_at
|
|
122
|
+
RETURNING a.*, (a.last_sent_at = a.last_seen_at) AS sent
|
|
123
|
+
),
|
|
124
|
+
dels AS (
|
|
125
|
+
INSERT INTO pipework_alert_deliveries (alert_key, kind, sink, payload, created_at, next_attempt_at)
|
|
126
|
+
SELECT up.key, CASE WHEN up.count = 1 THEN 'raise' ELSE 'repeat' END, s.sink,
|
|
127
|
+
${payloadSql(sql `CASE WHEN up.count = 1 THEN 'raise' ELSE 'repeat' END`)}, clock_timestamp(), clock_timestamp()
|
|
128
|
+
FROM up CROSS JOIN unnest(${sinkNames}::text[]) AS s(sink)
|
|
129
|
+
WHERE up.sent
|
|
130
|
+
RETURNING id
|
|
131
|
+
)
|
|
132
|
+
SELECT up.*, (SELECT count(*) FROM dels)::int AS queued FROM up`);
|
|
133
|
+
const row = rows[0];
|
|
134
|
+
const alert = toAlert(row);
|
|
135
|
+
const sent = row['sent'] === true;
|
|
136
|
+
const status = !sent ? 'grouped' : alert.count === 1 ? 'opened' : 'resent';
|
|
137
|
+
await auditAlert(status === 'opened' ? 'raise' : 'group', key, {
|
|
138
|
+
status, severity: alert.severity, count: alert.count, message: alert.message,
|
|
139
|
+
}, database);
|
|
140
|
+
if (sent && enqueue)
|
|
141
|
+
await enqueueJob();
|
|
142
|
+
return { status };
|
|
143
|
+
}
|
|
144
|
+
async function resolveKey(key, onlyIfQuiet, enqueue = true) {
|
|
145
|
+
const rows = await db().execute(sql `
|
|
146
|
+
WITH up AS (
|
|
147
|
+
UPDATE pipework_alerts SET status = 'resolved', resolved_at = clock_timestamp(), updated_at = clock_timestamp()
|
|
148
|
+
WHERE key = ${key} AND status = 'open'
|
|
149
|
+
AND (NOT ${onlyIfQuiet} OR last_seen_at < clock_timestamp()
|
|
150
|
+
- COALESCE(resolve_after_quiet_ms, ${defaultQuiet}::int) * interval '1 millisecond')
|
|
151
|
+
RETURNING *
|
|
152
|
+
),
|
|
153
|
+
dels AS (
|
|
154
|
+
INSERT INTO pipework_alert_deliveries (alert_key, kind, sink, payload, created_at, next_attempt_at)
|
|
155
|
+
SELECT up.key, 'resolve', s.sink, ${payloadSql(sql `'resolve'`)}, clock_timestamp(), clock_timestamp()
|
|
156
|
+
FROM up CROSS JOIN unnest(${sinkNames}::text[]) AS s(sink)
|
|
157
|
+
RETURNING id
|
|
158
|
+
)
|
|
159
|
+
SELECT up.*, (SELECT count(*) FROM dels)::int AS queued FROM up`);
|
|
160
|
+
if (rows.length === 0)
|
|
161
|
+
return false;
|
|
162
|
+
const alert = toAlert(rows[0]);
|
|
163
|
+
await auditAlert('resolve', key, { count: alert.count, auto: onlyIfQuiet }, database);
|
|
164
|
+
if (enqueue)
|
|
165
|
+
await enqueueJob();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
async function autoResolve() {
|
|
169
|
+
if (defaultQuiet === null) {
|
|
170
|
+
const any = await db().execute(sql `SELECT 1 FROM pipework_alerts WHERE status = 'open' AND resolve_after_quiet_ms IS NOT NULL LIMIT 1`);
|
|
171
|
+
if (any.length === 0)
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
const due = await db().execute(sql `
|
|
175
|
+
SELECT key FROM pipework_alerts
|
|
176
|
+
WHERE status = 'open'
|
|
177
|
+
AND last_seen_at < clock_timestamp() - COALESCE(resolve_after_quiet_ms, ${defaultQuiet}::int) * interval '1 millisecond'`);
|
|
178
|
+
let resolved = 0;
|
|
179
|
+
for (const row of due)
|
|
180
|
+
if (await resolveKey(row['key'], true, false))
|
|
181
|
+
resolved++;
|
|
182
|
+
return resolved;
|
|
183
|
+
}
|
|
184
|
+
async function runSources() {
|
|
185
|
+
let resolved = 0;
|
|
186
|
+
for (const source of sources) {
|
|
187
|
+
let conditions;
|
|
188
|
+
try {
|
|
189
|
+
conditions = await source.evaluate();
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
log.error({ err, source: source.name }, 'alert source failed');
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
for (const c of conditions) {
|
|
196
|
+
await raise(c.key, { severity: c.severity, message: c.message, ...(c.subject !== undefined ? { subject: c.subject } : {}) }, false);
|
|
197
|
+
}
|
|
198
|
+
if (source.owns === undefined)
|
|
199
|
+
continue;
|
|
200
|
+
const live = new Set(conditions.map(c => c.key));
|
|
201
|
+
const opened = await db().execute(sql `SELECT key FROM pipework_alerts WHERE status = 'open'`);
|
|
202
|
+
for (const row of opened) {
|
|
203
|
+
const key = row['key'];
|
|
204
|
+
if (source.owns(key) && !live.has(key) && await resolveKey(key, false, false))
|
|
205
|
+
resolved++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return resolved;
|
|
209
|
+
}
|
|
210
|
+
// Leases the due rows by pushing next_attempt_at out, so no row lock is held across a send. A row is
|
|
211
|
+
// due only if no older undelivered row of the same alert and sink exists: a resolve never overtakes its raise.
|
|
212
|
+
async function claimDue(dueBefore) {
|
|
213
|
+
return [...await db().execute(sql `
|
|
214
|
+
UPDATE pipework_alert_deliveries SET next_attempt_at = clock_timestamp() + ${LEASE_MS} * interval '1 millisecond'
|
|
215
|
+
WHERE id IN (
|
|
216
|
+
SELECT d.id FROM pipework_alert_deliveries d
|
|
217
|
+
WHERE d.delivered_at IS NULL AND d.failed_at IS NULL AND d.next_attempt_at <= ${dueBefore}::timestamptz
|
|
218
|
+
AND NOT EXISTS (
|
|
219
|
+
SELECT 1 FROM pipework_alert_deliveries o
|
|
220
|
+
WHERE o.alert_key = d.alert_key AND o.sink = d.sink AND o.delivered_at IS NULL AND o.failed_at IS NULL
|
|
221
|
+
AND (o.created_at, o.id) < (d.created_at, d.id))
|
|
222
|
+
ORDER BY d.next_attempt_at LIMIT ${BATCH} FOR UPDATE OF d SKIP LOCKED)
|
|
223
|
+
RETURNING *`)];
|
|
224
|
+
}
|
|
225
|
+
async function deliverBatch(batch, totals) {
|
|
226
|
+
await Promise.all(batch.map(async (row) => {
|
|
227
|
+
const id = row['id'];
|
|
228
|
+
const key = row['alert_key'];
|
|
229
|
+
const sinkName = row['sink'];
|
|
230
|
+
const kind = row['kind'];
|
|
231
|
+
const attempt = Number(row['attempts']) + 1;
|
|
232
|
+
const sink = sinks.get(sinkName);
|
|
233
|
+
let error = null;
|
|
234
|
+
if (sink === undefined) {
|
|
235
|
+
error = `sink "${sinkName}" is not configured`;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
try {
|
|
239
|
+
await withTimeout(sink.send({ id, kind, key, sink: sinkName, payload: row['payload'], attempt }), SEND_TIMEOUT_MS);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
error = err instanceof Error ? err.message : String(err);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (error === null) {
|
|
246
|
+
await db().execute(sql `UPDATE pipework_alert_deliveries SET delivered_at = clock_timestamp(), attempts = ${attempt}, last_error = NULL WHERE id = ${id}::uuid`);
|
|
247
|
+
await auditAlert('deliver', key, { sink: sinkName, kind, attempt }, database);
|
|
248
|
+
totals.delivered++;
|
|
249
|
+
}
|
|
250
|
+
else if (sink === undefined || attempt >= maxAttempts) {
|
|
251
|
+
await db().execute(sql `UPDATE pipework_alert_deliveries SET failed_at = clock_timestamp(), attempts = ${attempt}, last_error = ${error} WHERE id = ${id}::uuid`);
|
|
252
|
+
log.error({ key, sink: sinkName, kind, attempt, error }, 'alert delivery given up');
|
|
253
|
+
await auditAlert('deliver-failed', key, { sink: sinkName, kind, attempt, error }, database);
|
|
254
|
+
totals.failed++;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const delay = Math.min(backoffMs * 2 ** (attempt - 1), MAX_BACKOFF_MS);
|
|
258
|
+
await db().execute(sql `UPDATE pipework_alert_deliveries SET attempts = ${attempt}, last_error = ${error},
|
|
259
|
+
next_attempt_at = clock_timestamp() + ${delay} * interval '1 millisecond' WHERE id = ${id}::uuid`);
|
|
260
|
+
totals.retried++;
|
|
261
|
+
}
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
async function runTick() {
|
|
265
|
+
const totals = { delivered: 0, retried: 0, failed: 0 };
|
|
266
|
+
let resolved = await autoResolve();
|
|
267
|
+
resolved += await runSources();
|
|
268
|
+
// Rows that fail during this tick wait for the next one, however short their backoff.
|
|
269
|
+
const dueBefore = String((await db().execute(sql `SELECT clock_timestamp()::text AS t`))[0]['t']);
|
|
270
|
+
for (let i = 0; i < MAX_BATCHES; i++) {
|
|
271
|
+
const batch = await claimDue(dueBefore);
|
|
272
|
+
if (batch.length === 0)
|
|
273
|
+
break;
|
|
274
|
+
await deliverBatch(batch, totals);
|
|
275
|
+
}
|
|
276
|
+
return { resolved, ...totals };
|
|
277
|
+
}
|
|
278
|
+
const tickStep = __flowStep({ ref: 'pipework/alerts:tick' }, runTick);
|
|
279
|
+
const job = fitting.use().job(jobType).fit(async () => tickStep());
|
|
280
|
+
return {
|
|
281
|
+
raise: (key, options) => raise(key, options),
|
|
282
|
+
resolve: key => resolveKey(key, false),
|
|
283
|
+
job: job,
|
|
284
|
+
tick: () => tickStep(),
|
|
285
|
+
async open() {
|
|
286
|
+
const rows = await db().execute(sql `SELECT * FROM pipework_alerts WHERE status = 'open' ORDER BY first_seen_at, key`);
|
|
287
|
+
return rows.map(toAlert);
|
|
288
|
+
},
|
|
289
|
+
async get(key) {
|
|
290
|
+
const rows = await db().execute(sql `SELECT * FROM pipework_alerts WHERE key = ${key}`);
|
|
291
|
+
return rows[0] === undefined ? null : toAlert(rows[0]);
|
|
292
|
+
},
|
|
293
|
+
async deliveries(options = {}) {
|
|
294
|
+
const rows = options.key === undefined
|
|
295
|
+
? await db().execute(sql `SELECT * FROM pipework_alert_deliveries ORDER BY created_at, id`)
|
|
296
|
+
: await db().execute(sql `SELECT * FROM pipework_alert_deliveries WHERE alert_key = ${options.key} ORDER BY created_at, id`);
|
|
297
|
+
return rows.map(toDeliveryRow);
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle.js","sourceRoot":"","sources":["../../../src/infra/alerts/handle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createAlerts } from './handle.js';
|
|
2
|
+
export type { Alerts, AlertsConfig, Alert, AlertSeverity, AlertSubject, RaiseOptions, DeliveryRow, TickResult, } from './handle.js';
|
|
3
|
+
export type { AlertSink, AlertDelivery, AlertPayload, AlertWebhookConfig, MemoryAlertSink } from './sinks.js';
|
|
4
|
+
export type { AlertSource, AlertCondition } from './sources.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infra/alerts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EACV,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,GAChG,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC7G,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infra/alerts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createAlerts } from './handle.js';
|
|
2
|
+
import { webhookSink, memorySink } from './sinks.js';
|
|
3
|
+
import { deadLetterSource, longRunningSource, backlogSource, healthSource } from './sources.js';
|
|
4
|
+
/** Alerts namespace — raise and resolve keyed conditions, grouped, delivered to sinks through an outbox, traced. */
|
|
5
|
+
export declare const alerts: {
|
|
6
|
+
/** Creates an alerts handle. */
|
|
7
|
+
readonly create: typeof createAlerts;
|
|
8
|
+
/** A sink that POSTs the alert as signed JSON to a URL. */
|
|
9
|
+
readonly webhook: typeof webhookSink;
|
|
10
|
+
/** A sink that keeps deliveries in `.delivered`, for tests. */
|
|
11
|
+
readonly memory: typeof memorySink;
|
|
12
|
+
readonly source: {
|
|
13
|
+
/** Raises `job-dead-letter:<jobType>` for jobs that ran out of attempts. */
|
|
14
|
+
readonly deadLetter: typeof deadLetterSource;
|
|
15
|
+
/** Raises `job-long-running:<jobType>` while a job has been claimed too long. */
|
|
16
|
+
readonly longRunning: typeof longRunningSource;
|
|
17
|
+
/** Raises `queue-backlog:<jobType>` while too many jobs wait or the oldest has waited too long. */
|
|
18
|
+
readonly backlog: typeof backlogSource;
|
|
19
|
+
/** Raises `health:<name>` while a health check fails. */
|
|
20
|
+
readonly health: typeof healthSource;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=namespace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/infra/alerts/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE/F,oHAAoH;AACpH,eAAO,MAAM,MAAM;IACjB,gCAAgC;;IAEhC,2DAA2D;;IAE3D,+DAA+D;;;QAG7D,4EAA4E;;QAE5E,iFAAiF;;QAEjF,mGAAmG;;QAEnG,yDAAyD;;;CAGnD,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createAlerts } from './handle.js';
|
|
2
|
+
import { webhookSink, memorySink } from './sinks.js';
|
|
3
|
+
import { deadLetterSource, longRunningSource, backlogSource, healthSource } from './sources.js';
|
|
4
|
+
/** Alerts namespace — raise and resolve keyed conditions, grouped, delivered to sinks through an outbox, traced. */
|
|
5
|
+
export const alerts = {
|
|
6
|
+
/** Creates an alerts handle. */
|
|
7
|
+
create: createAlerts,
|
|
8
|
+
/** A sink that POSTs the alert as signed JSON to a URL. */
|
|
9
|
+
webhook: webhookSink,
|
|
10
|
+
/** A sink that keeps deliveries in `.delivered`, for tests. */
|
|
11
|
+
memory: memorySink,
|
|
12
|
+
source: {
|
|
13
|
+
/** Raises `job-dead-letter:<jobType>` for jobs that ran out of attempts. */
|
|
14
|
+
deadLetter: deadLetterSource,
|
|
15
|
+
/** Raises `job-long-running:<jobType>` while a job has been claimed too long. */
|
|
16
|
+
longRunning: longRunningSource,
|
|
17
|
+
/** Raises `queue-backlog:<jobType>` while too many jobs wait or the oldest has waited too long. */
|
|
18
|
+
backlog: backlogSource,
|
|
19
|
+
/** Raises `health:<name>` while a health check fails. */
|
|
20
|
+
health: healthSource,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=namespace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/infra/alerts/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE/F,oHAAoH;AACpH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,gCAAgC;IAChC,MAAM,EAAE,YAAY;IACpB,2DAA2D;IAC3D,OAAO,EAAE,WAAW;IACpB,+DAA+D;IAC/D,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE;QACN,4EAA4E;QAC5E,UAAU,EAAE,gBAAgB;QAC5B,iFAAiF;QACjF,WAAW,EAAE,iBAAiB;QAC9B,mGAAmG;QACnG,OAAO,EAAE,aAAa;QACtB,yDAAyD;QACzD,MAAM,EAAE,YAAY;KACrB;CACO,CAAA"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export declare const ALERT_STATUSES: readonly ["open", "resolved"];
|
|
2
|
+
export declare const ALERT_SEVERITIES: readonly ["info", "warning", "error", "critical"];
|
|
3
|
+
export declare const ALERT_DELIVERY_KINDS: readonly ["raise", "repeat", "resolve"];
|
|
4
|
+
export declare const alerts: import("../../data/domain/types.js").DefinedTable<{
|
|
5
|
+
key: import("../../data/domain/types.js").FieldDescriptor<string, {
|
|
6
|
+
primaryKey: true;
|
|
7
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string, {
|
|
8
|
+
primaryKey: true;
|
|
9
|
+
}>;
|
|
10
|
+
status: import("../../data/domain/types.js").FieldDescriptor<"open" | "resolved", {
|
|
11
|
+
hasDefault: true;
|
|
12
|
+
}> & import("../../data/domain/field.js").FieldModifiers<"open" | "resolved", {
|
|
13
|
+
hasDefault: true;
|
|
14
|
+
}>;
|
|
15
|
+
severity: import("../../data/domain/types.js").FieldDescriptor<"error" | "info" | "warning" | "critical", {}> & import("../../data/domain/field.js").FieldModifiers<"error" | "info" | "warning" | "critical", {}>;
|
|
16
|
+
message: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
17
|
+
subject: import("../../data/domain/types.js").FieldDescriptor<import("zod/v4").JSONType, {
|
|
18
|
+
nullable: true;
|
|
19
|
+
}> & import("../../data/domain/field.js").FieldModifiers<import("zod/v4").JSONType, {
|
|
20
|
+
nullable: true;
|
|
21
|
+
}>;
|
|
22
|
+
count: import("../../data/domain/types.js").FieldDescriptor<number, {
|
|
23
|
+
hasDefault: true;
|
|
24
|
+
}> & import("../../data/domain/field.js").FieldModifiers<number, {
|
|
25
|
+
hasDefault: true;
|
|
26
|
+
}>;
|
|
27
|
+
firstSeenAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
28
|
+
hasDefault: true;
|
|
29
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
30
|
+
hasDefault: true;
|
|
31
|
+
}>;
|
|
32
|
+
lastSeenAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
33
|
+
hasDefault: true;
|
|
34
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
35
|
+
hasDefault: true;
|
|
36
|
+
}>;
|
|
37
|
+
lastSentAt: import("../../data/domain/types.js").FieldDescriptor<Date | null, {
|
|
38
|
+
nullable: true;
|
|
39
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date | null, {
|
|
40
|
+
nullable: true;
|
|
41
|
+
}>;
|
|
42
|
+
resolvedAt: import("../../data/domain/types.js").FieldDescriptor<Date | null, {
|
|
43
|
+
nullable: true;
|
|
44
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date | null, {
|
|
45
|
+
nullable: true;
|
|
46
|
+
}>;
|
|
47
|
+
traceIds: import("../../data/domain/types.js").FieldDescriptor<string[], {
|
|
48
|
+
hasDefault: true;
|
|
49
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string[], {
|
|
50
|
+
hasDefault: true;
|
|
51
|
+
}>;
|
|
52
|
+
resendAfterMs: import("../../data/domain/types.js").FieldDescriptor<number | null, {
|
|
53
|
+
nullable: true;
|
|
54
|
+
}> & import("../../data/domain/field.js").FieldModifiers<number | null, {
|
|
55
|
+
nullable: true;
|
|
56
|
+
}>;
|
|
57
|
+
resolveAfterQuietMs: import("../../data/domain/types.js").FieldDescriptor<number | null, {
|
|
58
|
+
nullable: true;
|
|
59
|
+
}> & import("../../data/domain/field.js").FieldModifiers<number | null, {
|
|
60
|
+
nullable: true;
|
|
61
|
+
}>;
|
|
62
|
+
updatedAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
63
|
+
hasDefault: true;
|
|
64
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
65
|
+
hasDefault: true;
|
|
66
|
+
}>;
|
|
67
|
+
}, "pipework_alerts">;
|
|
68
|
+
export declare const alertDeliveries: import("../../data/domain/types.js").DefinedTable<{
|
|
69
|
+
id: import("../../data/domain/types.js").FieldDescriptor<string, {
|
|
70
|
+
defaultRandom: true;
|
|
71
|
+
hasDefault: true;
|
|
72
|
+
} & {
|
|
73
|
+
primaryKey: true;
|
|
74
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string, {
|
|
75
|
+
defaultRandom: true;
|
|
76
|
+
hasDefault: true;
|
|
77
|
+
} & {
|
|
78
|
+
primaryKey: true;
|
|
79
|
+
}>;
|
|
80
|
+
alertKey: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
81
|
+
kind: import("../../data/domain/types.js").FieldDescriptor<"repeat" | "raise" | "resolve", {}> & import("../../data/domain/field.js").FieldModifiers<"repeat" | "raise" | "resolve", {}>;
|
|
82
|
+
sink: import("../../data/domain/types.js").FieldDescriptor<string, {}> & import("../../data/domain/field.js").FieldModifiers<string, {}>;
|
|
83
|
+
payload: import("../../data/domain/types.js").FieldDescriptor<import("zod/v4").JSONType, {}> & import("../../data/domain/field.js").FieldModifiers<import("zod/v4").JSONType, {}>;
|
|
84
|
+
attempts: import("../../data/domain/types.js").FieldDescriptor<number, {
|
|
85
|
+
hasDefault: true;
|
|
86
|
+
}> & import("../../data/domain/field.js").FieldModifiers<number, {
|
|
87
|
+
hasDefault: true;
|
|
88
|
+
}>;
|
|
89
|
+
nextAttemptAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
90
|
+
hasDefault: true;
|
|
91
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
92
|
+
hasDefault: true;
|
|
93
|
+
}>;
|
|
94
|
+
deliveredAt: import("../../data/domain/types.js").FieldDescriptor<Date | null, {
|
|
95
|
+
nullable: true;
|
|
96
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date | null, {
|
|
97
|
+
nullable: true;
|
|
98
|
+
}>;
|
|
99
|
+
failedAt: import("../../data/domain/types.js").FieldDescriptor<Date | null, {
|
|
100
|
+
nullable: true;
|
|
101
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date | null, {
|
|
102
|
+
nullable: true;
|
|
103
|
+
}>;
|
|
104
|
+
lastError: import("../../data/domain/types.js").FieldDescriptor<string | null, {
|
|
105
|
+
nullable: true;
|
|
106
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string | null, {
|
|
107
|
+
nullable: true;
|
|
108
|
+
}>;
|
|
109
|
+
createdAt: import("../../data/domain/types.js").FieldDescriptor<Date, {
|
|
110
|
+
hasDefault: true;
|
|
111
|
+
}> & import("../../data/domain/field.js").FieldModifiers<Date, {
|
|
112
|
+
hasDefault: true;
|
|
113
|
+
}>;
|
|
114
|
+
}, "pipework_alert_deliveries">;
|
|
115
|
+
export declare const alertSourceState: import("../../data/domain/types.js").DefinedTable<{
|
|
116
|
+
name: import("../../data/domain/types.js").FieldDescriptor<string, {
|
|
117
|
+
primaryKey: true;
|
|
118
|
+
}> & import("../../data/domain/field.js").FieldModifiers<string, {
|
|
119
|
+
primaryKey: true;
|
|
120
|
+
}>;
|
|
121
|
+
cursorAt: import("../../data/domain/types.js").FieldDescriptor<Date, {}> & import("../../data/domain/field.js").FieldModifiers<Date, {}>;
|
|
122
|
+
}, "pipework_alert_source_state">;
|
|
123
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/infra/alerts/schema.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,cAAc,+BAAgC,CAAA;AAC3D,eAAO,MAAM,gBAAgB,mDAAoD,CAAA;AACjF,eAAO,MAAM,oBAAoB,yCAA0C,CAAA;AAE3E,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmBjB,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAmB1B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;iCAG3B,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// The tables behind alerts: one row per alert key, the outbox of deliveries (one row per
|
|
2
|
+
// event and sink), and the cursor a source keeps between evaluations. Not tenant-scoped.
|
|
3
|
+
import { define } from '../../data/domain/index.js';
|
|
4
|
+
import { field } from '../../data/domain/field.js';
|
|
5
|
+
import { schema } from '../../data/schema/namespace.js';
|
|
6
|
+
import { index } from '../../data/db/idx/index.js';
|
|
7
|
+
import { sql } from '../../data/db/sql.js';
|
|
8
|
+
export const ALERT_STATUSES = ['open', 'resolved'];
|
|
9
|
+
export const ALERT_SEVERITIES = ['info', 'warning', 'error', 'critical'];
|
|
10
|
+
export const ALERT_DELIVERY_KINDS = ['raise', 'repeat', 'resolve'];
|
|
11
|
+
export const alerts = define('pipework_alerts', {
|
|
12
|
+
key: field.text().primaryKey(),
|
|
13
|
+
status: field.enum(ALERT_STATUSES).default('open'),
|
|
14
|
+
severity: field.enum(ALERT_SEVERITIES),
|
|
15
|
+
message: field.text(),
|
|
16
|
+
subject: field.jsonb(schema.check.json()).nullable(),
|
|
17
|
+
count: field.integer().default(1),
|
|
18
|
+
firstSeenAt: field.timestamp().default('now'),
|
|
19
|
+
lastSeenAt: field.timestamp().default('now'),
|
|
20
|
+
lastSentAt: field.timestamp().nullable(),
|
|
21
|
+
resolvedAt: field.timestamp().nullable(),
|
|
22
|
+
traceIds: field.custom(schema.col.text('trace_ids').array(), schema.check.array(schema.check.string())).default('{}'),
|
|
23
|
+
resendAfterMs: field.integer().nullable(),
|
|
24
|
+
resolveAfterQuietMs: field.integer().nullable(),
|
|
25
|
+
updatedAt: field.timestamp().default('now'),
|
|
26
|
+
}, {
|
|
27
|
+
indexes: (t) => ({
|
|
28
|
+
byStatus: index('idx_pipework_alerts_status').on(t['status']),
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
export const alertDeliveries = define('pipework_alert_deliveries', {
|
|
32
|
+
id: field.uuid().defaultRandom().primaryKey(),
|
|
33
|
+
alertKey: field.text(),
|
|
34
|
+
kind: field.enum(ALERT_DELIVERY_KINDS),
|
|
35
|
+
sink: field.text(),
|
|
36
|
+
payload: field.jsonb(schema.check.json()),
|
|
37
|
+
attempts: field.integer().default(0),
|
|
38
|
+
nextAttemptAt: field.timestamp().default('now'),
|
|
39
|
+
deliveredAt: field.timestamp().nullable(),
|
|
40
|
+
failedAt: field.timestamp().nullable(),
|
|
41
|
+
lastError: field.text().nullable(),
|
|
42
|
+
createdAt: field.timestamp().default('now'),
|
|
43
|
+
}, {
|
|
44
|
+
indexes: (t) => ({
|
|
45
|
+
due: index('idx_pipework_alert_deliveries_due')
|
|
46
|
+
.on(t['nextAttemptAt'])
|
|
47
|
+
.where(sql.raw('delivered_at IS NULL AND failed_at IS NULL')),
|
|
48
|
+
byKey: index('idx_pipework_alert_deliveries_key').on(t['alertKey']),
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
export const alertSourceState = define('pipework_alert_source_state', {
|
|
52
|
+
name: field.text().primaryKey(),
|
|
53
|
+
cursorAt: field.timestamp(),
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/infra/alerts/schema.ts"],"names":[],"mappings":"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"}
|