@dudousxd/nestjs-durable-telescope 0.9.3 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +312 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +310 -8
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,9 @@ __export(index_exports, {
|
|
|
26
26
|
durableDashboard: () => durableDashboard,
|
|
27
27
|
durableDurationProvider: () => durableDurationProvider,
|
|
28
28
|
durableExecutionScope: () => durableExecutionScope,
|
|
29
|
+
durableFailedTriggersProvider: () => durableFailedTriggersProvider,
|
|
30
|
+
durableLifecycleTriggersProvider: () => durableLifecycleTriggersProvider,
|
|
31
|
+
durableOwedLifecycleProvider: () => durableOwedLifecycleProvider,
|
|
29
32
|
durableRecentFailuresProvider: () => durableRecentFailuresProvider,
|
|
30
33
|
durableRunsOverTimeProvider: () => durableRunsOverTimeProvider,
|
|
31
34
|
durableStateBreakdownProvider: () => durableStateBreakdownProvider,
|
|
@@ -37,6 +40,7 @@ __export(index_exports, {
|
|
|
37
40
|
durableTraceContext: () => durableTraceContext,
|
|
38
41
|
durableWorkerHealthProvider: () => durableWorkerHealthProvider,
|
|
39
42
|
durableWorkerStatusProvider: () => durableWorkerStatusProvider,
|
|
43
|
+
runSpanContext: () => runSpanContext,
|
|
40
44
|
runTraceContext: () => runTraceContext,
|
|
41
45
|
runTraceId: () => runTraceId
|
|
42
46
|
});
|
|
@@ -82,14 +86,18 @@ function runIds(runId) {
|
|
|
82
86
|
}
|
|
83
87
|
__name(runIds, "runIds");
|
|
84
88
|
function runTraceContext(runId, parent = import_api.context.active()) {
|
|
85
|
-
return import_api.trace.setSpanContext(parent,
|
|
89
|
+
return import_api.trace.setSpanContext(parent, runSpanContext(runId));
|
|
90
|
+
}
|
|
91
|
+
__name(runTraceContext, "runTraceContext");
|
|
92
|
+
function runSpanContext(runId) {
|
|
93
|
+
return {
|
|
86
94
|
traceId: runTraceId(runId),
|
|
87
95
|
spanId: runSpanId(runId),
|
|
88
96
|
traceFlags: import_api.TraceFlags.SAMPLED,
|
|
89
97
|
isRemote: true
|
|
90
|
-
}
|
|
98
|
+
};
|
|
91
99
|
}
|
|
92
|
-
__name(
|
|
100
|
+
__name(runSpanContext, "runSpanContext");
|
|
93
101
|
|
|
94
102
|
// src/durable-execution-scope.ts
|
|
95
103
|
var activeScope = new import_node_async_hooks.AsyncLocalStorage();
|
|
@@ -176,7 +184,9 @@ var RUN_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
176
184
|
"run.started",
|
|
177
185
|
"run.completed",
|
|
178
186
|
"run.failed",
|
|
179
|
-
"run.suspended"
|
|
187
|
+
"run.suspended",
|
|
188
|
+
"run.triggered",
|
|
189
|
+
"run.trigger_failed"
|
|
180
190
|
]);
|
|
181
191
|
|
|
182
192
|
// src/durable-telescope.watcher.ts
|
|
@@ -200,6 +210,7 @@ var DurableTelescopeWatcher = class {
|
|
|
200
210
|
engine.subscribe((event) => {
|
|
201
211
|
if (isRemote(event)) return;
|
|
202
212
|
const family = runEventFamily(event.type);
|
|
213
|
+
const source = lifecycleSourceOfEvent(event);
|
|
203
214
|
const record = /* @__PURE__ */ __name(() => ctx.record({
|
|
204
215
|
type: this.type,
|
|
205
216
|
...family !== void 0 ? {
|
|
@@ -223,7 +234,13 @@ var DurableTelescopeWatcher = class {
|
|
|
223
234
|
output: event.output,
|
|
224
235
|
error: event.error,
|
|
225
236
|
durationMs: event.durationMs,
|
|
226
|
-
reason: event.reason
|
|
237
|
+
reason: event.reason,
|
|
238
|
+
triggeredRunId: event.triggeredRunId,
|
|
239
|
+
...source ? {
|
|
240
|
+
lifecycleSourceRunId: source.runId,
|
|
241
|
+
lifecycleKind: source.kind,
|
|
242
|
+
lifecycleSourceTraceId: runTraceId(source.runId)
|
|
243
|
+
} : {}
|
|
227
244
|
},
|
|
228
245
|
tags: this.tags(event)
|
|
229
246
|
}), "record");
|
|
@@ -247,6 +264,21 @@ var DurableTelescopeWatcher = class {
|
|
|
247
264
|
}
|
|
248
265
|
import_api3.context.with(import_api3.trace.setSpan(base, step), record);
|
|
249
266
|
step.end();
|
|
267
|
+
} else if (source) {
|
|
268
|
+
const started = tracer.startSpan(`run ${event.workflow ?? event.runId}`, {
|
|
269
|
+
attributes: {
|
|
270
|
+
"durable.run_id": event.runId,
|
|
271
|
+
"durable.lifecycle.source_run_id": source.runId,
|
|
272
|
+
"durable.lifecycle.kind": source.kind
|
|
273
|
+
},
|
|
274
|
+
links: [
|
|
275
|
+
{
|
|
276
|
+
context: runSpanContext(source.runId)
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}, base);
|
|
280
|
+
import_api3.context.with(import_api3.trace.setSpan(base, started), record);
|
|
281
|
+
started.end();
|
|
250
282
|
} else {
|
|
251
283
|
import_api3.context.with(base, record);
|
|
252
284
|
}
|
|
@@ -277,9 +309,28 @@ var DurableTelescopeWatcher = class {
|
|
|
277
309
|
tags.push(event.type === "run.routed" ? "routed" : "unrouted");
|
|
278
310
|
tags.push(`onError:${event.error?.code ?? "*"}`);
|
|
279
311
|
}
|
|
312
|
+
if (event.type === "run.triggered") {
|
|
313
|
+
tags.push("triggered");
|
|
314
|
+
if (event.triggeredRunId) tags.push(`run:${event.triggeredRunId}`);
|
|
315
|
+
}
|
|
316
|
+
if (event.type === "run.trigger_failed") tags.push("trigger_failed", `subscriber:${event.name}`);
|
|
317
|
+
const source = lifecycleSourceOfEvent(event);
|
|
318
|
+
if (source) {
|
|
319
|
+
tags.push("lifecycle", `source:${source.runId}`);
|
|
320
|
+
const traceId = (0, import_nestjs_durable_core.traceIdOfTraceparent)(event.sourceTraceparent);
|
|
321
|
+
if (traceId) tags.push(`sourceTrace:${traceId}`);
|
|
322
|
+
}
|
|
280
323
|
return tags;
|
|
281
324
|
}
|
|
282
325
|
};
|
|
326
|
+
function lifecycleSourceOfEvent(event) {
|
|
327
|
+
if (event.type !== "run.started" || !event.workflow) return void 0;
|
|
328
|
+
return (0, import_nestjs_durable_core.lifecycleSourceOf)({
|
|
329
|
+
id: event.runId,
|
|
330
|
+
workflow: event.workflow
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
__name(lifecycleSourceOfEvent, "lifecycleSourceOfEvent");
|
|
283
334
|
|
|
284
335
|
// src/durable-telescope.extension.ts
|
|
285
336
|
var import_nestjs_telescope2 = require("@dudousxd/nestjs-telescope");
|
|
@@ -401,6 +452,66 @@ function durableDashboard(opts = {}) {
|
|
|
401
452
|
}
|
|
402
453
|
]
|
|
403
454
|
},
|
|
455
|
+
{
|
|
456
|
+
kind: "table",
|
|
457
|
+
title: "Failed triggers",
|
|
458
|
+
data: {
|
|
459
|
+
provider: "durable.failedTriggers"
|
|
460
|
+
},
|
|
461
|
+
columns: [
|
|
462
|
+
{
|
|
463
|
+
key: "at",
|
|
464
|
+
label: "Time"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
key: "workflow",
|
|
468
|
+
label: "Source workflow"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
key: "runId",
|
|
472
|
+
label: "Source run",
|
|
473
|
+
link: {
|
|
474
|
+
href: runHref
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
key: "subscriber",
|
|
479
|
+
label: "Subscriber"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
key: "error",
|
|
483
|
+
label: "Error"
|
|
484
|
+
}
|
|
485
|
+
]
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
kind: "table",
|
|
489
|
+
title: "Owed lifecycle events",
|
|
490
|
+
data: {
|
|
491
|
+
provider: "durable.owedLifecycle"
|
|
492
|
+
},
|
|
493
|
+
columns: [
|
|
494
|
+
{
|
|
495
|
+
key: "settledAt",
|
|
496
|
+
label: "Settled"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
key: "workflow",
|
|
500
|
+
label: "Workflow"
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
key: "runId",
|
|
504
|
+
label: "Run",
|
|
505
|
+
link: {
|
|
506
|
+
href: runHref
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
key: "owed",
|
|
511
|
+
label: "Owed to"
|
|
512
|
+
}
|
|
513
|
+
]
|
|
514
|
+
},
|
|
404
515
|
{
|
|
405
516
|
kind: "table",
|
|
406
517
|
// Lists ALL worker groups (starved ones sorted first); the Status column
|
|
@@ -428,6 +539,52 @@ function durableDashboard(opts = {}) {
|
|
|
428
539
|
label: "Status"
|
|
429
540
|
}
|
|
430
541
|
]
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
kind: "stat",
|
|
545
|
+
title: "Owed runs",
|
|
546
|
+
data: {
|
|
547
|
+
provider: "durable.owedLifecycle",
|
|
548
|
+
query: {
|
|
549
|
+
metric: "count"
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
spark: false,
|
|
553
|
+
thresholds: {
|
|
554
|
+
warn: 1,
|
|
555
|
+
bad: 50,
|
|
556
|
+
direction: "up-bad"
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
]
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
// Workflows started when another one settles (`run.completed:<wf>` / `run.failed:<wf>`).
|
|
563
|
+
title: "Lifecycle",
|
|
564
|
+
cols: 2,
|
|
565
|
+
panels: [
|
|
566
|
+
{
|
|
567
|
+
kind: "timeseries",
|
|
568
|
+
title: "Lifecycle triggers over time",
|
|
569
|
+
data: {
|
|
570
|
+
provider: "durable.lifecycleTriggers"
|
|
571
|
+
},
|
|
572
|
+
series: [
|
|
573
|
+
"triggered",
|
|
574
|
+
"failed"
|
|
575
|
+
],
|
|
576
|
+
style: "stacked"
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
kind: "topN",
|
|
580
|
+
title: "Triggered by subscriber",
|
|
581
|
+
data: {
|
|
582
|
+
provider: "durable.lifecycleTriggers",
|
|
583
|
+
query: {
|
|
584
|
+
metric: "bySubscriber"
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
limit: 10
|
|
431
588
|
}
|
|
432
589
|
]
|
|
433
590
|
},
|
|
@@ -1076,6 +1233,148 @@ function durableStateBreakdownProvider() {
|
|
|
1076
1233
|
};
|
|
1077
1234
|
}
|
|
1078
1235
|
__name(durableStateBreakdownProvider, "durableStateBreakdownProvider");
|
|
1236
|
+
function stamp(at) {
|
|
1237
|
+
return at ? `${new Date(at).toISOString().replace("T", " ").slice(0, 16)}Z` : "";
|
|
1238
|
+
}
|
|
1239
|
+
__name(stamp, "stamp");
|
|
1240
|
+
async function triggerFailures(ctx, limit) {
|
|
1241
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1242
|
+
const out = [];
|
|
1243
|
+
for (const entry of await fetchRunEntries(ctx, [
|
|
1244
|
+
"run.trigger_failed"
|
|
1245
|
+
], limit)) {
|
|
1246
|
+
const c = entry.content ?? {};
|
|
1247
|
+
if (c.event !== "run.trigger_failed") continue;
|
|
1248
|
+
const key = `${c.runId}\0${c.name}`;
|
|
1249
|
+
if (seen.has(key)) continue;
|
|
1250
|
+
seen.add(key);
|
|
1251
|
+
out.push(entry);
|
|
1252
|
+
}
|
|
1253
|
+
return out;
|
|
1254
|
+
}
|
|
1255
|
+
__name(triggerFailures, "triggerFailures");
|
|
1256
|
+
function durableFailedTriggersProvider() {
|
|
1257
|
+
return {
|
|
1258
|
+
name: "durable.failedTriggers",
|
|
1259
|
+
async resolve(query, ctx) {
|
|
1260
|
+
const limit = Math.min(200, Math.max(10, Number(query?.limit ?? 50)));
|
|
1261
|
+
const windowMs = query?.windowMs === void 0 ? 24 * 60 * 60 * 1e3 : Number(query.windowMs);
|
|
1262
|
+
const cutoff = windowMs > 0 ? Date.now() - windowMs : 0;
|
|
1263
|
+
const rows = (await triggerFailures(ctx, RUN_EVENTS_LIMIT)).filter((entry) => entryTime(entry) >= cutoff).slice(0, limit).map((entry) => {
|
|
1264
|
+
const c = entry.content ?? {};
|
|
1265
|
+
return {
|
|
1266
|
+
at: stamp(entry.createdAt),
|
|
1267
|
+
workflow: c.workflow ?? "",
|
|
1268
|
+
runId: c.runId ?? "",
|
|
1269
|
+
subscriber: c.name ?? "",
|
|
1270
|
+
error: c.error?.message ?? ""
|
|
1271
|
+
};
|
|
1272
|
+
});
|
|
1273
|
+
return {
|
|
1274
|
+
rows
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
__name(durableFailedTriggersProvider, "durableFailedTriggersProvider");
|
|
1280
|
+
var LIFECYCLE_GRACE_MS = 3e4;
|
|
1281
|
+
function durableOwedLifecycleProvider() {
|
|
1282
|
+
return {
|
|
1283
|
+
name: "durable.owedLifecycle",
|
|
1284
|
+
async resolve(query, ctx) {
|
|
1285
|
+
const store = ctx.moduleRef.get(import_nestjs_durable_core2.STATE_STORE_CANONICAL, {
|
|
1286
|
+
strict: false
|
|
1287
|
+
});
|
|
1288
|
+
const limit = Math.min(200, Math.max(10, Number(query?.limit ?? 50)));
|
|
1289
|
+
const cutoff = Date.now() - LIFECYCLE_GRACE_MS;
|
|
1290
|
+
const runs = await store.listRuns({
|
|
1291
|
+
tag: import_nestjs_durable_core2.LIFECYCLE_PENDING_TAG,
|
|
1292
|
+
statuses: [
|
|
1293
|
+
"completed",
|
|
1294
|
+
"failed",
|
|
1295
|
+
"cancelled",
|
|
1296
|
+
"dead"
|
|
1297
|
+
],
|
|
1298
|
+
limit: 1e3
|
|
1299
|
+
});
|
|
1300
|
+
const owed = runs.filter((run) => +new Date(run.updatedAt) <= cutoff);
|
|
1301
|
+
if (query?.metric === "count") return {
|
|
1302
|
+
value: owed.length
|
|
1303
|
+
};
|
|
1304
|
+
const rows = owed.sort((a, b) => +new Date(b.updatedAt) - +new Date(a.updatedAt)).slice(0, limit).map((run) => ({
|
|
1305
|
+
settledAt: stamp(run.updatedAt),
|
|
1306
|
+
workflow: run.workflow,
|
|
1307
|
+
runId: run.id,
|
|
1308
|
+
owed: (0, import_nestjs_durable_core2.owedSubscribersOf)(run.tags).join(", ") || "every subscriber"
|
|
1309
|
+
}));
|
|
1310
|
+
return {
|
|
1311
|
+
rows
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
__name(durableOwedLifecycleProvider, "durableOwedLifecycleProvider");
|
|
1317
|
+
function durableLifecycleTriggersProvider() {
|
|
1318
|
+
return {
|
|
1319
|
+
name: "durable.lifecycleTriggers",
|
|
1320
|
+
async resolve(query, ctx) {
|
|
1321
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1322
|
+
const entries = (await fetchRunEntries(ctx, [
|
|
1323
|
+
"run.triggered",
|
|
1324
|
+
"run.trigger_failed"
|
|
1325
|
+
])).filter((entry) => {
|
|
1326
|
+
const c = entry.content ?? {};
|
|
1327
|
+
if (c.event !== "run.triggered" && c.event !== "run.trigger_failed") return false;
|
|
1328
|
+
const key = `${c.event}\0${c.runId}\0${c.name}`;
|
|
1329
|
+
if (seen.has(key)) return false;
|
|
1330
|
+
seen.add(key);
|
|
1331
|
+
return true;
|
|
1332
|
+
});
|
|
1333
|
+
if (query?.metric === "bySubscriber") {
|
|
1334
|
+
const limit = Math.max(1, Number(query?.limit ?? 10));
|
|
1335
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1336
|
+
for (const entry of entries) {
|
|
1337
|
+
const c = entry.content ?? {};
|
|
1338
|
+
if (c.event !== "run.triggered") continue;
|
|
1339
|
+
const label = `${c.workflow ?? "unknown"} \u2192 ${c.name ?? "unknown"}`;
|
|
1340
|
+
counts.set(label, (counts.get(label) ?? 0) + 1);
|
|
1341
|
+
}
|
|
1342
|
+
const items = [
|
|
1343
|
+
...counts
|
|
1344
|
+
].map(([label, value]) => ({
|
|
1345
|
+
label,
|
|
1346
|
+
value
|
|
1347
|
+
})).sort((a, b) => b.value - a.value || a.label.localeCompare(b.label)).slice(0, limit);
|
|
1348
|
+
return {
|
|
1349
|
+
items
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
const n = Math.max(1, Number(query?.buckets ?? 24));
|
|
1353
|
+
const now = Date.now();
|
|
1354
|
+
let minT = now;
|
|
1355
|
+
for (const entry of entries) if (entry.createdAt) minT = Math.min(minT, entryTime(entry));
|
|
1356
|
+
const bucketSize = Math.max(now - minT, 1) / n;
|
|
1357
|
+
const rows = Array.from({
|
|
1358
|
+
length: n
|
|
1359
|
+
}, (_, i) => ({
|
|
1360
|
+
label: new Date(minT + i * bucketSize).toISOString().slice(11, 16),
|
|
1361
|
+
triggered: 0,
|
|
1362
|
+
failed: 0
|
|
1363
|
+
}));
|
|
1364
|
+
for (const entry of entries) {
|
|
1365
|
+
const index = Math.floor((entryTime(entry) - minT) / bucketSize);
|
|
1366
|
+
const row = rows[Math.min(n - 1, Math.max(0, index))];
|
|
1367
|
+
if (!row) continue;
|
|
1368
|
+
if (entry.content.event === "run.triggered") row.triggered += 1;
|
|
1369
|
+
else row.failed += 1;
|
|
1370
|
+
}
|
|
1371
|
+
return {
|
|
1372
|
+
rows
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
__name(durableLifecycleTriggersProvider, "durableLifecycleTriggersProvider");
|
|
1079
1378
|
|
|
1080
1379
|
// src/durable-telescope.extension.ts
|
|
1081
1380
|
function durableTelescopeExtension(opts = {}) {
|
|
@@ -1104,7 +1403,10 @@ function durableTelescopeExtension(opts = {}) {
|
|
|
1104
1403
|
durableRunsOverTimeProvider(),
|
|
1105
1404
|
durableSuccessRateProvider(),
|
|
1106
1405
|
durableThroughputProvider(),
|
|
1107
|
-
durableStateBreakdownProvider()
|
|
1406
|
+
durableStateBreakdownProvider(),
|
|
1407
|
+
durableFailedTriggersProvider(),
|
|
1408
|
+
durableOwedLifecycleProvider(),
|
|
1409
|
+
durableLifecycleTriggersProvider()
|
|
1108
1410
|
], "dataProviders")
|
|
1109
1411
|
});
|
|
1110
1412
|
}
|
|
@@ -1116,6 +1418,9 @@ __name(durableTelescopeExtension, "durableTelescopeExtension");
|
|
|
1116
1418
|
durableDashboard,
|
|
1117
1419
|
durableDurationProvider,
|
|
1118
1420
|
durableExecutionScope,
|
|
1421
|
+
durableFailedTriggersProvider,
|
|
1422
|
+
durableLifecycleTriggersProvider,
|
|
1423
|
+
durableOwedLifecycleProvider,
|
|
1119
1424
|
durableRecentFailuresProvider,
|
|
1120
1425
|
durableRunsOverTimeProvider,
|
|
1121
1426
|
durableStateBreakdownProvider,
|
|
@@ -1127,6 +1432,7 @@ __name(durableTelescopeExtension, "durableTelescopeExtension");
|
|
|
1127
1432
|
durableTraceContext,
|
|
1128
1433
|
durableWorkerHealthProvider,
|
|
1129
1434
|
durableWorkerStatusProvider,
|
|
1435
|
+
runSpanContext,
|
|
1130
1436
|
runTraceContext,
|
|
1131
1437
|
runTraceId
|
|
1132
1438
|
});
|