-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathsyntheticTrace.server.ts
More file actions
79 lines (75 loc) · 3.02 KB
/
Copy pathsyntheticTrace.server.ts
File metadata and controls
79 lines (75 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { millisecondsToNanoseconds } from "@trigger.dev/core/v3";
import { createTreeFromFlatItems, flattenTree } from "~/components/primitives/TreeView/TreeView";
import { createTimelineSpanEventsFromSpanEvents } from "~/utils/timelineSpanEvents";
import type { SpanSummary } from "~/v3/eventRepository/eventRepository.types";
import type { SyntheticRun } from "./readFallback.server";
// Build a single-span trace for a buffered run so the run-detail page
// renders a meaningful timeline before the drainer materialises the
// row. Mirrors the shape produced by `RunPresenter` when its trace
// store lookup returns no spans, so the dashboard consumer treats the
// buffered run identically to a freshly enqueued PG run that hasn't
// emitted any events yet.
export function buildSyntheticTraceForBufferedRun(run: SyntheticRun) {
const spanId = run.spanId ?? "";
const isCancelled = run.status === "CANCELED";
const isFailed = run.status === "FAILED";
const span: SpanSummary = {
id: spanId,
parentId: run.parentSpanId,
runId: run.friendlyId,
data: {
message: run.taskIdentifier ?? "Task",
style: { icon: "task", variant: "primary" },
events: [],
startTime: run.createdAt,
duration: 0,
isError: isFailed,
// CANCELED and FAILED are terminal; only a still-queued buffered run
// is partial. A partial failed span would otherwise render as
// "executing" forever in the timeline.
isPartial: !isCancelled && !isFailed,
isCancelled,
isDebug: false,
level: "TRACE",
},
};
const tree = createTreeFromFlatItems([span], spanId);
const treeRootStartTimeMs = tree?.data.startTime.getTime() ?? 0;
const totalDuration = Math.max(tree?.data.duration ?? 0, millisecondsToNanoseconds(1));
const events = tree
? flattenTree(tree).map((n) => {
const offset = millisecondsToNanoseconds(
n.data.startTime.getTime() - treeRootStartTimeMs
);
// Mirror RunPresenter: raw span events stay server-side, only
// timelineEvents ship to the client.
const { events: spanEvents, ...data } = n.data;
return {
...n,
data: {
...data,
timelineEvents: createTimelineSpanEventsFromSpanEvents(spanEvents, false, treeRootStartTimeMs),
duration: n.data.isPartial ? null : n.data.duration,
offset,
isRoot: n.id === spanId,
},
};
})
: [];
return {
// Matches RunPresenter's derivation: failed root span -> "failed",
// otherwise a terminal (non-partial) span -> "completed", else
// "executing". CANCELED is terminal-but-not-error, so "completed".
rootSpanStatus: (isFailed ? "failed" : isCancelled ? "completed" : "executing") as
| "executing"
| "completed"
| "failed",
events,
duration: totalDuration,
rootStartedAt: tree?.data.startTime,
startedAt: null,
queuedDuration: undefined,
overridesBySpanId: undefined,
linkedRunIdBySpanId: {} as Record<string, string>,
};
}