Skip to content

Commit ebfc133

Browse files
fix: display build timings when all timings are loaded (coder#15728)
- [Refetch timings until script timings are present](coder@2181bec) - [Stay on loading state when agent script timings are empty](coder@b16fad1) Fix coder#15273
1 parent c7c35ef commit ebfc133

File tree

4 files changed

+43
-18
lines changed

4 files changed

+43
-18
lines changed

site/src/api/queries/workspaceBuilds.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,9 @@ export const infiniteWorkspaceBuilds = (
5858
};
5959

6060
// We use readyAgentsCount to invalidate the query when an agent connects
61-
export const workspaceBuildTimings = (
62-
workspaceBuildId: string,
63-
readyAgentsCount: number,
64-
) => {
61+
export const workspaceBuildTimings = (workspaceBuildId: string) => {
6562
return {
66-
queryKey: [
67-
"workspaceBuilds",
68-
workspaceBuildId,
69-
"timings",
70-
{ readyAgentsCount },
71-
],
63+
queryKey: ["workspaceBuilds", workspaceBuildId, "timings"],
7264
queryFn: () => API.workspaceBuildTimings(workspaceBuildId),
7365
};
7466
};

site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.stories.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,11 @@ export const DuplicatedScriptTiming: Story = {
118118
],
119119
},
120120
};
121+
122+
// Loading when agent script timings are empty
123+
// Test case for https://github.com/coder/coder/issues/15273
124+
export const LoadingWhenAgentScriptTimingsAreEmpty: Story = {
125+
args: {
126+
agentScriptTimings: undefined,
127+
},
128+
};

site/src/modules/workspaces/WorkspaceTiming/WorkspaceTimings.tsx

+14-3
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,25 @@ export const WorkspaceTimings: FC<WorkspaceTimingsProps> = ({
5858
].sort((a, b) => {
5959
return new Date(a.started_at).getTime() - new Date(b.started_at).getTime();
6060
});
61+
6162
const [isOpen, setIsOpen] = useState(defaultIsOpen);
62-
const isLoading = timings.length === 0;
6363

64-
// All stages
64+
// If any of the timings are empty, we are still loading the data. They can be
65+
// filled in different moments.
66+
const isLoading = [
67+
provisionerTimings,
68+
agentScriptTimings,
69+
agentConnectionTimings,
70+
].some((t) => t.length === 0);
71+
72+
// Each agent connection timing is a stage in the timeline to make it easier
73+
// to users to see the timing for connection and the other scripts.
6574
const agentStageLabels = Array.from(
6675
new Set(
6776
agentConnectionTimings.map((t) => `agent (${t.workspace_agent_name})`),
6877
),
6978
);
79+
7080
const stages = [
7181
...provisioningStages,
7282
...agentStageLabels.flatMap((a) => agentStages(a)),
@@ -120,7 +130,8 @@ export const WorkspaceTimings: FC<WorkspaceTimingsProps> = ({
120130
: mergeTimeRanges(stageTimings.map(toTimeRange));
121131

122132
// Prevent users from inspecting internal coder resources in
123-
// provisioner timings.
133+
// provisioner timings because they were not useful to the
134+
// user and would add noise.
124135
const visibleResources = stageTimings.filter((t) => {
125136
const isProvisionerTiming = "resource" in t;
126137
return isProvisionerTiming

site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx

+19-5
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,27 @@ export const WorkspaceReadyPage: FC<WorkspaceReadyPageProps> = ({
157157
// Cancel build
158158
const cancelBuildMutation = useMutation(cancelBuild(workspace, queryClient));
159159

160-
// Build Timings. Fetch build timings only when the build job is completed.
161-
const readyAgents = workspace.latest_build.resources
162-
.flatMap((r) => r.agents)
163-
.filter((a) => a && a.lifecycle_state !== "starting");
160+
// Workspace Timings.
164161
const timingsQuery = useQuery({
165-
...workspaceBuildTimings(workspace.latest_build.id, readyAgents.length),
162+
...workspaceBuildTimings(workspace.latest_build.id),
163+
164+
// Fetch build timings only when the build job is completed.
166165
enabled: Boolean(workspace.latest_build.job.completed_at),
166+
167+
// Sometimes, the timings can be fetched before the agent script timings are
168+
// done or saved in the database so we need to conditionally refetch the
169+
// timings. To refetch the timings, I found the best way was to compare the
170+
// expected amount of script timings with the current amount of script
171+
// timings returned in the response.
172+
refetchInterval: (data) => {
173+
const expectedScriptTimingsCount = workspace.latest_build.resources
174+
.flatMap((r) => r.agents)
175+
.flatMap((a) => a?.scripts ?? []).length;
176+
const currentScriptTimingsCount = data?.agent_script_timings?.length ?? 0;
177+
return expectedScriptTimingsCount === currentScriptTimingsCount
178+
? false
179+
: 1_000;
180+
},
167181
});
168182

169183
const runLastBuild = (

0 commit comments

Comments
 (0)