Skip to content

Commit bf8e516

Browse files
Arsal34isakstenstrom
authored andcommitted
Fix behavior on missing data in CAS
- FindMissingBlob used to identify when a blob is missing from cas - Updated the following screens: - Browser (Display an error message if the data is missing) Command Action Historical Execute Response - Compare Action (Allows new comparison in case of missing data) - CAS Log Viewer (Displays that the data is missing) - Critical Path (Hide critical path) - Profile (Disable the button but display the reason through a ToolTip)
1 parent 74de764 commit bf8e516

10 files changed

Lines changed: 263 additions & 48 deletions

File tree

frontend/src/components/BrowserActionPages/index.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { initialSizeClassCacheClient } from "@/grpc/initialSizeClassCacheClient"
1111
import type { BrowserSearchParams } from "@/routes/browser.$";
1212
import type { BrowserPageParams } from "@/types/BrowserPageParams";
1313
import { BrowserPageType } from "@/types/BrowserPageType";
14+
import { useCheckDataExists } from "@/utils/fetchCasObject";
1415
import BrowserActionGrid from "../BrowserActionGrid";
1516
import { fetchBrowserActionGrid } from "../BrowserActionGrid/fetch";
1617
import { CompareActionButtons } from "../CompareAction/CompareTargetButton";
@@ -26,6 +27,13 @@ interface Params {
2627
export const BrowserActionPages: React.FC<Params> = ({ params, search }) => {
2728
const [prefersCompareSideBySide, setPrefersCompareSideBySide] =
2829
useState(true);
30+
31+
const { exists, isLoading } = useCheckDataExists(
32+
params.instanceName,
33+
[params.digest],
34+
params.digestFunction,
35+
);
36+
2937
const actionQuery = useQuery({
3038
queryKey: ["browserActionGrid", params.digest.hash],
3139
queryFn: () => {
@@ -38,10 +46,12 @@ export const BrowserActionPages: React.FC<Params> = ({ params, search }) => {
3846
);
3947
},
4048
staleTime: 5 * 60 * 1000,
49+
enabled: exists === true,
4150
});
51+
4252
const compareActionQuery = useQuery({
4353
queryKey: ["browserActionGrid", search.comparedAction?.digest.hash],
44-
enabled: !!search.comparedAction,
54+
enabled: !!search.comparedAction && exists === true,
4555
queryFn: () => {
4656
if (!search.comparedAction) {
4757
return undefined;
@@ -57,6 +67,17 @@ export const BrowserActionPages: React.FC<Params> = ({ params, search }) => {
5767
staleTime: 5 * 60 * 1000,
5868
});
5969

70+
if (exists === false && !isLoading) {
71+
return (
72+
<PortalAlert
73+
showIcon
74+
type="error"
75+
title="Error fetching the operation"
76+
description={"The CAS contains no data for this action"}
77+
/>
78+
);
79+
}
80+
6081
if (
6182
actionQuery.isPending ||
6283
(compareActionQuery.isPending && search.comparedAction)
@@ -69,16 +90,22 @@ export const BrowserActionPages: React.FC<Params> = ({ params, search }) => {
6990
compareActionQuery.isError ||
7091
(!compareActionQuery.data && search.comparedAction)
7192
) {
93+
let errorMessage: string;
94+
if (
95+
actionQuery.error?.message.endsWith("Object not found") ||
96+
compareActionQuery.error?.message.endsWith("Object not found")
97+
) {
98+
errorMessage = "The target action was not found";
99+
} else {
100+
errorMessage =
101+
"Unknown error occurred while fetching data from the server.";
102+
}
72103
return (
73104
<PortalAlert
74105
showIcon
75106
type="error"
76-
title="Error fetching action"
77-
description={
78-
actionQuery.error?.message ||
79-
compareActionQuery.error?.message ||
80-
"Unknown error occurred while fetching data from the server."
81-
}
107+
title="Error fetching the operation"
108+
description={errorMessage}
82109
/>
83110
);
84111
}

frontend/src/components/BrowserCommandGrid/index.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import type React from "react";
44
import { casByteStreamClient } from "@/grpc/casByteStreamClient";
55
import { Command } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution";
66
import type { BrowserPageParams } from "@/types/BrowserPageParams";
7-
import { fetchCasObjectAndParse } from "@/utils/fetchCasObject";
7+
import {
8+
fetchCasObjectAndParse,
9+
useCheckDataExists,
10+
} from "@/utils/fetchCasObject";
811
import { BrowserCommandDescription } from "../BrowserCommandDescription";
912
import CopyBbClientdCommandButton from "../BrowserCommandDescription/CopyBbClientdCommandButton";
1013
import DownloadAsShellScriptButton from "../BrowserCommandDescription/DownloadAsShellScriptButton";
@@ -17,6 +20,12 @@ interface Params {
1720
}
1821

1922
const BrowserCommandGrid: React.FC<Params> = ({ browserPageParams }) => {
23+
const { exists, isLoading } = useCheckDataExists(
24+
browserPageParams.instanceName,
25+
[browserPageParams.digest],
26+
browserPageParams.digestFunction,
27+
);
28+
2029
const { data, isError, isPending, error } = useQuery({
2130
queryKey: ["browserCommandGrid", browserPageParams],
2231
queryFn: () =>
@@ -27,8 +36,20 @@ const BrowserCommandGrid: React.FC<Params> = ({ browserPageParams }) => {
2736
browserPageParams.digest,
2837
Command,
2938
),
39+
enabled: exists === true,
3040
});
3141

42+
if (!exists && !isLoading) {
43+
return (
44+
<PortalAlert
45+
showIcon
46+
type="error"
47+
title="Command not found"
48+
description="The CAS contains no data for this command."
49+
/>
50+
);
51+
}
52+
3253
if (isPending) {
3354
return <Spin />;
3455
}

frontend/src/components/BrowserResultDescription/index.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Descriptions, Space, Tag } from "antd";
1+
import { Descriptions, Space, Spin, Tag } from "antd";
22
import type React from "react";
33
import type { ExecuteResponse } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution";
44
import type { POSIXResourceUsage } from "@/lib/grpc-client/buildbarn/resourceusage/resourceusage";
55
import type { BrowserPageParams } from "@/types/BrowserPageParams";
66
import { digestFunctionValueToString } from "@/utils/digestFunctionUtils";
7+
import { useCheckDataExists } from "@/utils/fetchCasObject";
78
import { CasViewer } from "../LogViewer/casViewer";
9+
import PortalAlert from "../PortalAlert";
810

911
interface Params {
1012
browserPageParams: BrowserPageParams;
@@ -17,6 +19,38 @@ const BrowserResultDescription: React.FC<Params> = ({
1719
executeResponse,
1820
posixResourceUsage,
1921
}) => {
22+
const { exists: stdOutExists, isLoading: isLoadingOut } = useCheckDataExists(
23+
browserPageParams.instanceName,
24+
[
25+
{
26+
hash: executeResponse?.result?.stdoutDigest?.hash ?? "",
27+
sizeBytes: (
28+
executeResponse?.result?.stdoutDigest?.sizeBytes ?? ""
29+
).toString(),
30+
},
31+
],
32+
browserPageParams.digestFunction,
33+
executeResponse.result?.stdoutDigest !== undefined,
34+
);
35+
36+
const { exists: stdErrExists, isLoading: isLoadingErr } = useCheckDataExists(
37+
browserPageParams.instanceName,
38+
[
39+
{
40+
hash: executeResponse?.result?.stderrDigest?.hash ?? "",
41+
sizeBytes: (
42+
executeResponse?.result?.stderrDigest?.sizeBytes ?? ""
43+
).toString(),
44+
},
45+
],
46+
browserPageParams.digestFunction,
47+
executeResponse.result?.stdoutDigest !== undefined,
48+
);
49+
50+
if (isLoadingOut && isLoadingErr) {
51+
return <Spin />;
52+
}
53+
2054
const renderResult = () => {
2155
if (executeResponse.status !== undefined) {
2256
return (
@@ -59,7 +93,10 @@ const BrowserResultDescription: React.FC<Params> = ({
5993
>
6094
{renderResult()}
6195
</Descriptions>
62-
{executeResponse.result?.stdoutDigest?.hash &&
96+
97+
{!executeResponse.result?.stdoutDigest?.hash ? null : stdOutExists ===
98+
true ? (
99+
executeResponse.result?.stdoutDigest?.hash &&
63100
executeResponse.result?.stdoutDigest?.sizeBytes && (
64101
<CasViewer
65102
instanceName={browserPageParams.instanceName}
@@ -74,8 +111,19 @@ const BrowserResultDescription: React.FC<Params> = ({
74111
title="Standard Output"
75112
fileName="standard_output.txt"
76113
/>
77-
)}
78-
{executeResponse.result?.stderrDigest?.hash &&
114+
)
115+
) : (
116+
<PortalAlert
117+
type="error"
118+
title="Standard output logs not found"
119+
description="The data does not exist"
120+
showIcon
121+
/>
122+
)}
123+
124+
{!executeResponse.result?.stderrDigest?.hash ? null : stdErrExists ===
125+
true ? (
126+
executeResponse.result?.stderrDigest?.hash &&
79127
executeResponse.result?.stderrDigest?.sizeBytes && (
80128
<CasViewer
81129
instanceName={browserPageParams.instanceName}
@@ -90,7 +138,15 @@ const BrowserResultDescription: React.FC<Params> = ({
90138
title="Standard Error"
91139
fileName="standard_error.txt"
92140
/>
93-
)}
141+
)
142+
) : (
143+
<PortalAlert
144+
type="error"
145+
title="Standard error logs not found"
146+
description={`The data does not exist`}
147+
showIcon
148+
/>
149+
)}
94150
</Space>
95151
);
96152
};

frontend/src/components/CompareAction/CompareTargetButton/index.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { Button, Space } from "antd";
22
import { useEffect, useState } from "react";
33
import { LinkButton } from "@/components/LinkButton";
4+
import { digestFunction_ValueFromJSON } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution";
45
import {
56
type BrowserPageParams,
67
BrowserPageSchema,
78
} from "@/types/BrowserPageParams";
89
import { BrowserPageType } from "@/types/BrowserPageType";
10+
import { useCheckDataExists } from "@/utils/fetchCasObject";
911
import { generateBrowserSplat } from "@/utils/urlGenerator";
1012

1113
const COMPARE_KEY = "buildbarn_compare_action";
@@ -17,6 +19,19 @@ type Props = {
1719

1820
const CompareActionButtons: React.FC<Props> = ({ params, comparing }) => {
1921
const [storedData, setStoredData] = useState<BrowserPageParams | undefined>();
22+
23+
const { exists } = useCheckDataExists(
24+
storedData?.instanceName ?? "",
25+
[
26+
storedData?.digest ?? {
27+
hash: "",
28+
sizeBytes: "",
29+
},
30+
],
31+
storedData?.digestFunction ?? digestFunction_ValueFromJSON(""),
32+
storedData !== undefined,
33+
);
34+
2035
useEffect(() => {
2136
const checkSavedData = () => {
2237
try {
@@ -63,7 +78,8 @@ const CompareActionButtons: React.FC<Props> = ({ params, comparing }) => {
6378
</LinkButton>
6479
);
6580
}
66-
if (!storedData) {
81+
82+
if (!storedData || !exists) {
6783
return (
6884
<Button type="default" onClick={setCompare}>
6985
Compare...

frontend/src/components/InvocationOverviewDisplay/index.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { Descriptions } from "antd";
22
import type React from "react";
33
import { getFragmentData } from "@/graphql/__generated__";
44
import type { BazelInvocationOverviewFragment } from "@/graphql/__generated__/graphql";
5+
import { digestFunction_ValueFromJSON } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution";
56
import { FILE_DETAILS_FRAGMENT } from "@/types/GraphqlFileFragment";
67
import { commandLineDataToString } from "@/utils/commandLineDataToString";
8+
import { useCheckDataExists } from "@/utils/fetchCasObject";
79
import { CriticalPathDisplay } from "../CriticalPath";
810
import { InvocationResultTag } from "../InvocationResultTag";
911
import PortalDuration from "../PortalDuration";
@@ -52,6 +54,20 @@ export const InvocationOverviewDisplay: React.FC<Props> = ({ invocation }) => {
5254

5355
const parsedProfile = getFragmentData(FILE_DETAILS_FRAGMENT, profile);
5456

57+
const { exists } = useCheckDataExists(
58+
parsedProfile?.digest.rev2InstanceName ?? "",
59+
[
60+
{
61+
hash: parsedProfile?.digest.hash ?? "",
62+
sizeBytes: (parsedProfile?.digest.sizeBytes ?? "").toString(),
63+
},
64+
],
65+
digestFunction_ValueFromJSON(
66+
(parsedProfile?.digest.digestFunction ?? "").toUpperCase(),
67+
),
68+
parsedProfile !== undefined,
69+
);
70+
5571
return (
5672
<Descriptions column={1} bordered>
5773
<Descriptions.Item label="Status">
@@ -84,7 +100,7 @@ export const InvocationOverviewDisplay: React.FC<Props> = ({ invocation }) => {
84100
formatConfig={{ smallestUnit: "s" }}
85101
/>
86102
</Descriptions.Item>
87-
{parsedProfile && (
103+
{parsedProfile && exists && (
88104
<Descriptions.Item label="Critical Path">
89105
<CriticalPathDisplay
90106
profile={parsedProfile}

0 commit comments

Comments
 (0)