-
Notifications
You must be signed in to change notification settings - Fork 978
fix: let workspace pages download partial logs for unhealthy workspaces #13761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
f1f5f88
edd6362
901aa39
e1d8113
87f57aa
5bf2baf
5c2316b
edd6569
2eb7d6e
fa06515
8fb4496
6935f50
f74eda4
7b76eb7
07ff536
460aa53
5a449b4
eb4f88c
71692b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import { useTheme, type Interpolation, type Theme } from "@emotion/react"; | |
import Skeleton from "@mui/material/Skeleton"; | ||
import { saveAs } from "file-saver"; | ||
import JSZip from "jszip"; | ||
import { useMemo, useState, type FC, useRef, useEffect } from "react"; | ||
import { type FC, useMemo, useState, useRef, useEffect } from "react"; | ||
import { UseQueryOptions, useQueries, useQuery } from "react-query"; | ||
import { agentLogs, buildLogs } from "api/queries/workspaces"; | ||
import type { | ||
|
@@ -59,13 +59,14 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
// This is clunky, but we have to memoize in two steps to make sure that we | ||
// don't accidentally break the memo cache every render. We can't tuck | ||
// everything into a single memo call, because we need to set up React Query | ||
// state between processing the agents, and we can't violate rules of hooks | ||
// state between processing the agents, but we can't violate rules of hooks by | ||
// putting hooks inside of hooks | ||
type AgentInfo = Readonly<{ | ||
agents: readonly WorkspaceAgent[]; | ||
queries: readonly UseQueryOptions<readonly WorkspaceAgentLog[]>[]; | ||
logOptionsArray: readonly UseQueryOptions<readonly WorkspaceAgentLog[]>[]; | ||
}>; | ||
|
||
const { agents, queries } = useMemo<AgentInfo>(() => { | ||
const { agents, logOptionsArray } = useMemo<AgentInfo>(() => { | ||
const allAgents = workspace.latest_build.resources.flatMap( | ||
(resource) => resource.agents ?? [], | ||
); | ||
|
@@ -77,7 +78,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
|
||
return { | ||
agents: uniqueAgents, | ||
queries: uniqueAgents.map((agent) => { | ||
logOptionsArray: uniqueAgents.map((agent) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to calculate the query options here and not mount it and pass it in the query? I think it is kinda far from what is used and I took some time to link There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this was an over-optimization on my part. I basically figured that since the query objects shouldn't change unless the agents do, it was safe to throw them all together |
||
return { | ||
...agentLogs(workspace.id, agent.id), | ||
enabled: open, | ||
|
@@ -86,13 +87,13 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
}; | ||
}, [workspace, open]); | ||
|
||
const agentLogResults = useQueries({ queries }); | ||
const agentLogQueries = useQueries({ queries: logOptionsArray }); | ||
const allFiles = useMemo<readonly DownloadableFile[]>(() => { | ||
const files: DownloadableFile[] = [buildLogsFile]; | ||
|
||
agents.forEach((a, i) => { | ||
const name = `${a.name}-logs.txt`; | ||
const txt = agentLogResults[i]?.data?.map((l) => l.output).join("\n"); | ||
const txt = agentLogQueries[i]?.data?.map((l) => l.output).join("\n"); | ||
|
||
let blob: Blob | undefined; | ||
if (txt) { | ||
|
@@ -103,7 +104,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
}); | ||
|
||
return files; | ||
}, [agentLogResults, agents, buildLogsFile]); | ||
}, [agentLogQueries, agents, buildLogsFile]); | ||
|
||
const [isDownloading, setIsDownloading] = useState(false); | ||
const isWorkspaceHealthy = workspace.health.healthy; | ||
|
@@ -134,7 +135,7 @@ export const DownloadLogsDialog: FC<DownloadLogsDialogProps> = ({ | |
disabled={ | ||
isDownloading || | ||
// If a workspace isn't healthy, let the user download as many logs as | ||
// they can | ||
// they can. Otherwise, wait for everything to come in | ||
(isWorkspaceHealthy && isLoadingFiles) | ||
} | ||
onConfirm={async () => { | ||
|
@@ -214,14 +215,10 @@ const DownloadingItem: FC<DownloadingItemProps> = ({ file, giveUpTimeMs }) => { | |
<span css={styles.listItemSecondary}> | ||
{file.blob ? ( | ||
humanBlobSize(file.blob.size) | ||
) : isWaiting ? ( | ||
<Skeleton variant="text" width={48} height={12} /> | ||
) : ( | ||
<> | ||
{isWaiting ? ( | ||
<Skeleton variant="text" width={48} height={12} /> | ||
) : ( | ||
<p css={styles.notAvailableText}>N/A</p> | ||
)} | ||
</> | ||
<p css={styles.notAvailableText}>N/A</p> | ||
)} | ||
</span> | ||
</li> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but I feel it is an over-optimization. If it is fast enough and does not impact UI usage, I don't see why we should complicate things.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was considering that, but didn't really have time to test out how long it would take to keep generating blobs every render
To be clear, we basically have two choices:
The original memoization has worse performance than not having memoization at all, because we're still recalculating the blobs every render, but React has to compare dependencies and decide if it should re-calculate the value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it was my fault. I usually use "useMemo" to compute a variable value but it would probably be better to be a function. I would rather remove memorization altogether to make things simple.