Skip to content

Commit ccbeff4

Browse files
committed
Improve trace view performance
1 parent 7c8f2df commit ccbeff4

File tree

8 files changed

+239
-115
lines changed

8 files changed

+239
-115
lines changed

apps/webapp/app/components/primitives/TreeView/TreeView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ export type Tree<TData> = {
528528
/** A tree but flattened so it can easily be used for DOM elements */
529529
export type FlatTreeItem<TData> = {
530530
id: string;
531-
parentId: string | undefined;
531+
parentId?: string | undefined;
532532
children: string[];
533533
hasChildren: boolean;
534534
/** The indentation level, the root is 0 */

apps/webapp/app/entry.server.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,9 @@ export { apiRateLimiter } from "./services/apiRateLimit.server";
186186
export { socketIo } from "./v3/handleSocketIo.server";
187187
export { wss } from "./v3/handleWebsockets.server";
188188
export { registryProxy } from "./v3/registryProxy.server";
189-
export { eventLoopMonitor } from "./eventLoopMonitor.server";
189+
import { eventLoopMonitor } from "./eventLoopMonitor.server";
190+
import { env } from "./env.server";
191+
192+
if (env.EVENT_LOOP_MONITOR_ENABLED === "1") {
193+
eventLoopMonitor.enable();
194+
}

apps/webapp/app/env.server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ const EnvironmentSchema = z.object({
204204

205205
USAGE_OPEN_METER_API_KEY: z.string().optional(),
206206
USAGE_OPEN_METER_BASE_URL: z.string().optional(),
207+
EVENT_LOOP_MONITOR_ENABLED: z.string().default("1"),
207208
});
208209

209210
export type Environment = z.infer<typeof EnvironmentSchema>;
Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,77 @@
11
import { createHook } from "node:async_hooks";
2-
import { logger } from "./services/logger.server";
32
import { singleton } from "./utils/singleton";
3+
import { tracer } from "./v3/tracer.server";
44

55
const THRESHOLD_NS = 1e8; // 100ms
66

7-
const cache = new Map<number, [number, number]>();
7+
const cache = new Map<number, { type: string; start?: [number, number] }>();
8+
9+
function init(asyncId: number, type: string, triggerAsyncId: number, resource: any) {
10+
cache.set(asyncId, {
11+
type,
12+
});
13+
}
14+
15+
function destroy(asyncId: number) {
16+
cache.delete(asyncId);
17+
}
818

919
function before(asyncId: number) {
10-
cache.set(asyncId, process.hrtime());
20+
const cached = cache.get(asyncId);
21+
22+
if (!cached) {
23+
return;
24+
}
25+
26+
cache.set(asyncId, {
27+
...cached,
28+
start: process.hrtime(),
29+
});
1130
}
1231

1332
function after(asyncId: number) {
1433
const cached = cache.get(asyncId);
15-
if (cached == null) {
34+
35+
if (!cached) {
1636
return;
1737
}
38+
1839
cache.delete(asyncId);
1940

20-
const diff = process.hrtime(cached);
41+
if (!cached.start) {
42+
return;
43+
}
44+
45+
const diff = process.hrtime(cached.start);
2146
const diffNs = diff[0] * 1e9 + diff[1];
2247
if (diffNs > THRESHOLD_NS) {
2348
const time = diffNs / 1e6; // in ms
2449

25-
logger.error(`Event loop was blocked for ${time}ms`, {
26-
label: "EventLoopMonitor",
50+
const newSpan = tracer.startSpan("event-loop-blocked", {
2751
startTime: new Date(new Date().getTime() - time),
28-
durationMs: time,
52+
attributes: {
53+
asyncType: cached.type,
54+
label: "EventLoopMonitor",
55+
},
2956
});
57+
58+
newSpan.end();
3059
}
3160
}
3261

3362
export const eventLoopMonitor = singleton("eventLoopMonitor", () => {
34-
// console.log("🥸 Initializing event loop monitor");
35-
// const asyncHook = createHook({ before, after });
36-
// asyncHook.enable();
37-
// return asyncHook;
63+
const hook = createHook({ init, before, after, destroy });
64+
65+
return {
66+
enable: () => {
67+
console.log("🥸 Initializing event loop monitor");
68+
69+
hook.enable();
70+
},
71+
disable: () => {
72+
console.log("🥸 Disabling event loop monitor");
73+
74+
hook.disable();
75+
},
76+
};
3877
});

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
MagnifyingGlassPlusIcon,
77
} from "@heroicons/react/20/solid";
88
import type { Location } from "@remix-run/react";
9-
import { useParams, useRevalidator } from "@remix-run/react";
10-
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
9+
import { useLoaderData, useParams, useRevalidator } from "@remix-run/react";
10+
import { LoaderFunctionArgs, SerializeFrom } from "@remix-run/server-runtime";
1111
import { Virtualizer } from "@tanstack/react-virtual";
1212
import {
1313
formatDurationMilliseconds,
@@ -18,10 +18,10 @@ import { RuntimeEnvironmentType } from "@trigger.dev/database";
1818
import { motion } from "framer-motion";
1919
import { useCallback, useEffect, useRef, useState } from "react";
2020
import { useHotkeys } from "react-hotkeys-hook";
21-
import { typedjson, useTypedLoaderData } from "remix-typedjson";
2221
import { ShowParentIcon, ShowParentIconSelected } from "~/assets/icons/ShowParentIcon";
2322
import tileBgPath from "~/assets/images/[email protected]";
2423
import { BlankstateInstructions } from "~/components/BlankstateInstructions";
24+
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
2525
import { InlineCode } from "~/components/code/InlineCode";
2626
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
2727
import { MainCenteredContainer, PageBody } from "~/components/layout/AppLayout";
@@ -33,6 +33,7 @@ import { Input } from "~/components/primitives/Input";
3333
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
3434
import { Paragraph } from "~/components/primitives/Paragraph";
3535
import { Popover, PopoverArrowTrigger, PopoverContent } from "~/components/primitives/Popover";
36+
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
3637
import {
3738
ResizableHandle,
3839
ResizablePanel,
@@ -55,7 +56,7 @@ import { useProject } from "~/hooks/useProject";
5556
import { useReplaceLocation } from "~/hooks/useReplaceLocation";
5657
import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys";
5758
import { useUser } from "~/hooks/useUser";
58-
import { RunEvent, RunPresenter } from "~/presenters/v3/RunPresenter.server";
59+
import { RunPresenter } from "~/presenters/v3/RunPresenter.server";
5960
import { getResizableRunSettings, setResizableRunSettings } from "~/services/resizablePanel";
6061
import { requireUserId } from "~/services/session.server";
6162
import { cn } from "~/utils/cn";
@@ -68,12 +69,12 @@ import {
6869
v3RunsPath,
6970
} from "~/utils/pathBuilder";
7071
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
71-
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
72-
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
7372
import { SimpleTooltip } from "~/components/primitives/Tooltip";
7473

7574
const MAX_LIVE_RELOADING_EVENTS = 500;
7675

76+
type TraceEvent = NonNullable<SerializeFrom<typeof loader>["trace"]>["events"][0];
77+
7778
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
7879
const userId = await requireUserId(request);
7980
const { projectParam, organizationSlug, runParam } = v3RunParamsSchema.parse(params);
@@ -89,10 +90,11 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
8990
//resizable settings
9091
const resizeSettings = await getResizableRunSettings(request);
9192

92-
return typedjson({
93-
...result,
93+
return {
94+
run: result.run,
95+
trace: result.trace,
9496
resizeSettings,
95-
});
97+
};
9698
};
9799

98100
function getSpanId(location: Location<any>): string | undefined {
@@ -101,7 +103,7 @@ function getSpanId(location: Location<any>): string | undefined {
101103
}
102104

103105
export default function Page() {
104-
const { run, trace, resizeSettings } = useTypedLoaderData<typeof loader>();
106+
const { run, trace, resizeSettings } = useLoaderData<typeof loader>();
105107
const organization = useOrganization();
106108
const project = useProject();
107109
const user = useUser();
@@ -258,7 +260,7 @@ export default function Page() {
258260
}}
259261
totalDuration={duration}
260262
rootSpanStatus={rootSpanStatus}
261-
rootStartedAt={rootStartedAt}
263+
rootStartedAt={rootStartedAt ? new Date(rootStartedAt) : undefined}
262264
environmentType={run.environment.type}
263265
shouldLiveReload={shouldLiveReload}
264266
/>
@@ -281,7 +283,7 @@ export default function Page() {
281283
}
282284

283285
type TasksTreeViewProps = {
284-
events: RunEvent[];
286+
events: TraceEvent[];
285287
selectedId?: string;
286288
parentRunFriendlyId?: string;
287289
onSelectedIdChanged: (selectedId: string | undefined) => void;
@@ -762,7 +764,7 @@ function TimelineView({
762764
);
763765
}
764766

765-
function NodeText({ node }: { node: RunEvent }) {
767+
function NodeText({ node }: { node: TraceEvent }) {
766768
const className = "truncate";
767769
return (
768770
<Paragraph variant="small" className={cn(className)}>
@@ -771,7 +773,7 @@ function NodeText({ node }: { node: RunEvent }) {
771773
);
772774
}
773775

774-
function NodeStatusIcon({ node }: { node: RunEvent }) {
776+
function NodeStatusIcon({ node }: { node: TraceEvent }) {
775777
if (node.data.level !== "TRACE") return null;
776778
if (node.data.style.variant !== "primary") return null;
777779

@@ -896,7 +898,7 @@ function SpanWithDuration({
896898
showDuration,
897899
node,
898900
...props
899-
}: Timeline.SpanProps & { node: RunEvent; showDuration: boolean }) {
901+
}: Timeline.SpanProps & { node: TraceEvent; showDuration: boolean }) {
900902
return (
901903
<Timeline.Span {...props}>
902904
<motion.div

0 commit comments

Comments
 (0)