Skip to content

Commit fc57fb8

Browse files
committed
fix: Querying data unreliable with custom backend
**Changes:** * Clear backend query cache when backend settings change * Wait until we have determined the backend url fully before executing backend queries
1 parent 2e61db3 commit fc57fb8

File tree

9 files changed

+112
-33
lines changed

9 files changed

+112
-33
lines changed

src/components/Home/RunSection/RunSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {
7575

7676
const { data, isLoading, isFetching, error, isFetched } =
7777
useQuery<ListPipelineJobsResponse>({
78-
queryKey: ["runs", backendUrl, pageToken, search.filter],
78+
queryKey: ["runs", pageToken, search.filter],
7979
refetchOnWindowFocus: false,
8080
enabled: configured && available,
8181
queryFn: async () => {

src/components/PipelineRun/RunDetails.test.tsx

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ComponentSpecProvider } from "@/providers/ComponentSpecProvider";
1616
import { ContextPanelProvider } from "@/providers/ContextPanelProvider";
1717
import { ExecutionDataProvider } from "@/providers/ExecutionDataProvider";
1818
import type { ComponentSpec } from "@/utils/componentSpec";
19+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1920

2021
import { RunDetails } from "./RunDetails";
2122

@@ -62,6 +63,8 @@ describe("<RunDetails/>", () => {
6263
},
6364
});
6465

66+
const MOCK_BACKEND_URL = "http://localhost:8000";
67+
6568
const mockExecutionDetails: GetExecutionInfoResponse = {
6669
id: "test-execution-id",
6770
pipeline_run_id: "123",
@@ -133,13 +136,16 @@ describe("<RunDetails/>", () => {
133136
error: null,
134137
});
135138

136-
queryClient.setQueryData(["pipeline-run-metadata", "123"], mockPipelineRun);
139+
queryClient.setQueryData(
140+
[BACKEND_QUERY_KEY, "pipeline-run-metadata", "123"],
141+
mockPipelineRun,
142+
);
137143

138144
vi.mocked(useBackend).mockReturnValue({
139145
configured: true,
140146
available: true,
141147
ready: true,
142-
backendUrl: "http://localhost:8000",
148+
backendUrl: MOCK_BACKEND_URL,
143149
isConfiguredFromEnv: false,
144150
isConfiguredFromRelativePath: false,
145151
setEnvConfig: vi.fn(),
@@ -172,6 +178,50 @@ describe("<RunDetails/>", () => {
172178
});
173179
};
174180

181+
describe("Backend Configuration", () => {
182+
test("should render run details when backend is configured", async () => {
183+
// The default mock has configured: true and backendUrl: MOCK_BACKEND_URL
184+
// act
185+
renderWithProviders(<RunDetails />);
186+
187+
// assert
188+
await waitFor(() => {
189+
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
190+
expect(screen.getByText("Run Id:")).toBeInTheDocument();
191+
expect(screen.getByText("123")).toBeInTheDocument();
192+
});
193+
});
194+
195+
test("should render run details when backendUrl is empty string", async () => {
196+
// arrange - simulate custom backend toggle disabled (empty backendUrl)
197+
vi.mocked(useBackend).mockReturnValue({
198+
configured: true,
199+
available: true,
200+
ready: true,
201+
backendUrl: "",
202+
isConfiguredFromEnv: false,
203+
isConfiguredFromRelativePath: true,
204+
setEnvConfig: vi.fn(),
205+
setRelativePathConfig: vi.fn(),
206+
setBackendUrl: vi.fn(),
207+
ping: vi.fn(),
208+
});
209+
210+
// Query key no longer includes backendUrl - cache is shared regardless of URL
211+
// and invalidated when backend URL changes
212+
213+
// act
214+
renderWithProviders(<RunDetails />);
215+
216+
// assert
217+
await waitFor(() => {
218+
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
219+
expect(screen.getByText("Run Id:")).toBeInTheDocument();
220+
expect(screen.getByText("123")).toBeInTheDocument();
221+
});
222+
});
223+
});
224+
175225
describe("Inspect Pipeline Button", () => {
176226
test("should render inspect button when pipeline exists", async () => {
177227
// arrange
@@ -262,7 +312,7 @@ describe("<RunDetails/>", () => {
262312
};
263313

264314
queryClient.setQueryData(
265-
["pipeline-run-metadata", "123"],
315+
[BACKEND_QUERY_KEY, "pipeline-run-metadata", "123"],
266316
pipelineRunWithDifferentCreator,
267317
);
268318

src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskOverview/IOSection/IOSection.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useBackend } from "@/providers/BackendProvider";
88
import { getExecutionArtifacts } from "@/services/executionService";
99
import { getBackendStatusString } from "@/utils/backend";
1010
import type { TaskSpec } from "@/utils/componentSpec";
11+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1112

1213
import IOExtras from "./IOExtras";
1314
import IOInputs from "./IOInputs";
@@ -28,9 +29,9 @@ const IOSection = ({ taskSpec, executionId, readOnly }: IOSectionProps) => {
2829
isFetching,
2930
error,
3031
} = useQuery({
31-
queryKey: ["artifacts", executionId],
32+
queryKey: [BACKEND_QUERY_KEY, "artifacts", executionId],
3233
queryFn: () => getExecutionArtifacts(String(executionId), backendUrl),
33-
enabled: !!executionId,
34+
enabled: !!executionId && configured,
3435
});
3536

3637
if (!configured) {

src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskOverview/logs.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Link } from "@/components/ui/link";
77
import { Spinner } from "@/components/ui/spinner";
88
import { useBackend } from "@/providers/BackendProvider";
99
import { getBackendStatusString } from "@/utils/backend";
10+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1011

1112
const LogDisplay = ({
1213
logs,
@@ -108,10 +109,10 @@ const Logs = ({
108109
log_text?: string;
109110
system_error_exception_full?: string;
110111
}>();
111-
const { data, isLoading, error, refetch } = useQuery({
112-
queryKey: ["logs", executionId],
112+
const { data, isLoading, error } = useQuery({
113+
queryKey: [BACKEND_QUERY_KEY, "logs", executionId],
113114
queryFn: () => getLogs(String(executionId), backendUrl),
114-
enabled: isLogging,
115+
enabled: isLogging && configured,
115116
refetchInterval: 5000,
116117
refetchIntervalInBackground: false,
117118
});
@@ -136,10 +137,6 @@ const Logs = ({
136137
}
137138
}, [data, error]);
138139

139-
useEffect(() => {
140-
refetch();
141-
}, [backendUrl, refetch]);
142-
143140
if (!configured) {
144141
return (
145142
<InfoBox title="Backend not configured" variant="warning">

src/hooks/usePipelineRunData.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ import {
77
fetchExecutionState,
88
fetchPipelineRun,
99
} from "@/services/executionService";
10+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1011
import {
1112
flattenExecutionStatusStats,
1213
isExecutionComplete,
1314
} from "@/utils/executionStatus";
1415

1516
const useRootExecutionId = (id: string) => {
16-
const { backendUrl } = useBackend();
17+
const { backendUrl, configured } = useBackend();
1718
const { data: rootExecutionId } = useQuery({
18-
queryKey: ["pipeline-run-execution-id", id],
19+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-execution-id", id],
1920
queryFn: async () => {
2021
const rootExecutionId = await fetchPipelineRun(id, backendUrl)
2122
.then((res) => res.root_execution_id)
@@ -28,7 +29,7 @@ const useRootExecutionId = (id: string) => {
2829
// assuming id is root_execution_id
2930
return id;
3031
},
31-
enabled: !!id && id.length > 0,
32+
enabled: !!id && id.length > 0 && configured,
3233
staleTime: Infinity,
3334
});
3435

@@ -37,13 +38,13 @@ const useRootExecutionId = (id: string) => {
3738

3839
/* Accepts root_execution_id or run_id and returns execution details and state */
3940
export const usePipelineRunData = (id: string) => {
40-
const { backendUrl } = useBackend();
41+
const { backendUrl, configured } = useBackend();
4142

4243
const rootExecutionId = useRootExecutionId(id);
4344

4445
const { data: executionDetails } = useQuery({
45-
enabled: !!rootExecutionId,
46-
queryKey: ["execution-details", rootExecutionId],
46+
enabled: !!rootExecutionId && configured,
47+
queryKey: [BACKEND_QUERY_KEY, "execution-details", rootExecutionId],
4748
queryFn: async () => {
4849
if (!rootExecutionId) {
4950
throw new Error("No root execution id found");
@@ -59,8 +60,8 @@ export const usePipelineRunData = (id: string) => {
5960
error,
6061
isLoading,
6162
} = useQuery({
62-
enabled: !!rootExecutionId && !!executionDetails,
63-
queryKey: ["pipeline-run", rootExecutionId],
63+
enabled: !!rootExecutionId && !!executionDetails && configured,
64+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run", rootExecutionId],
6465
queryFn: async () => {
6566
if (!rootExecutionId) {
6667
throw new Error("No root execution id found");

src/hooks/useSubgraphBreadcrumbs.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useMemo } from "react";
44
import { useBackend } from "@/providers/BackendProvider";
55
import { fetchExecutionDetails } from "@/services/executionService";
66
import { isGraphImplementationOutput } from "@/utils/componentSpec";
7-
import { ONE_MINUTE_IN_MS } from "@/utils/constants";
7+
import { BACKEND_QUERY_KEY, ONE_MINUTE_IN_MS } from "@/utils/constants";
88

99
export interface BreadcrumbSegment {
1010
taskId: string;
@@ -27,11 +27,16 @@ export const useSubgraphBreadcrumbs = (
2727
rootExecutionId: string | undefined,
2828
subgraphExecutionId: string | undefined,
2929
): SubgraphBreadcrumbsResult => {
30-
const { backendUrl } = useBackend();
30+
const { backendUrl, configured } = useBackend();
3131
const queryClient = useQueryClient();
3232

3333
const { data, isLoading, error } = useQuery({
34-
queryKey: ["subgraph-breadcrumbs", rootExecutionId, subgraphExecutionId],
34+
queryKey: [
35+
BACKEND_QUERY_KEY,
36+
"subgraph-breadcrumbs",
37+
rootExecutionId,
38+
subgraphExecutionId,
39+
],
3540
queryFn: async () => {
3641
if (!rootExecutionId || !subgraphExecutionId) {
3742
return { segments: [] };
@@ -44,7 +49,7 @@ export const useSubgraphBreadcrumbs = (
4449
const segmentsInReverseOrder: BreadcrumbSegment[] = [];
4550
let currentExecutionId = subgraphExecutionId;
4651
let currentDetails = await queryClient.ensureQueryData({
47-
queryKey: ["execution-details", currentExecutionId],
52+
queryKey: [BACKEND_QUERY_KEY, "execution-details", currentExecutionId],
4853
queryFn: () => fetchExecutionDetails(currentExecutionId, backendUrl),
4954
staleTime: ONE_MINUTE_IN_MS,
5055
});
@@ -57,7 +62,7 @@ export const useSubgraphBreadcrumbs = (
5762
}
5863

5964
const parentDetails = await queryClient.ensureQueryData({
60-
queryKey: ["execution-details", parentExecutionId],
65+
queryKey: [BACKEND_QUERY_KEY, "execution-details", parentExecutionId],
6166
queryFn: () => fetchExecutionDetails(parentExecutionId, backendUrl),
6267
staleTime: ONE_MINUTE_IN_MS,
6368
});
@@ -95,7 +100,8 @@ export const useSubgraphBreadcrumbs = (
95100
enabled:
96101
!!rootExecutionId &&
97102
!!subgraphExecutionId &&
98-
rootExecutionId !== subgraphExecutionId,
103+
rootExecutionId !== subgraphExecutionId &&
104+
configured,
99105
staleTime: ONE_MINUTE_IN_MS,
100106
retry: 1,
101107
});

src/providers/BackendProvider.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import { useQueryClient } from "@tanstack/react-query";
12
import {
23
type ReactNode,
34
useCallback,
45
useEffect,
56
useMemo,
7+
useRef,
68
useState,
79
} from "react";
810

911
import useToastNotification from "@/hooks/useToastNotification";
10-
import { API_URL } from "@/utils/constants";
12+
import { API_URL, BACKEND_QUERY_KEY } from "@/utils/constants";
1113
import {
1214
getUseEnv,
1315
getUserBackendUrl,
@@ -45,6 +47,7 @@ const BackendContext =
4547

4648
export const BackendProvider = ({ children }: { children: ReactNode }) => {
4749
const notify = useToastNotification();
50+
const queryClient = useQueryClient();
4851

4952
const backendUrlFromEnv = API_URL;
5053

@@ -55,6 +58,9 @@ export const BackendProvider = ({ children }: { children: ReactNode }) => {
5558
const [settingsLoaded, setSettingsLoaded] = useState(false);
5659
const [ready, setReady] = useState(false);
5760

61+
// Track the previous backend URL to detect changes
62+
const previousBackendUrlRef = useRef<string | null>(null);
63+
5864
let backendUrl = "";
5965
if (useEnv && backendUrlFromEnv) {
6066
backendUrl = backendUrlFromEnv;
@@ -133,6 +139,17 @@ export const BackendProvider = ({ children }: { children: ReactNode }) => {
133139
}
134140
}, [backendUrl, settingsLoaded]);
135141

142+
// Invalidate only backend-dependent queries when the backend URL changes
143+
useEffect(() => {
144+
if (
145+
previousBackendUrlRef.current !== null &&
146+
previousBackendUrlRef.current !== backendUrl
147+
) {
148+
queryClient.invalidateQueries({ queryKey: [BACKEND_QUERY_KEY] });
149+
}
150+
previousBackendUrlRef.current = backendUrl;
151+
}, [backendUrl, queryClient]);
152+
136153
useEffect(() => {
137154
const getSettings = async () => {
138155
const url = await getUserBackendUrl();

src/services/executionService.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
} from "@/api/types.gen";
1010
import { useBackend } from "@/providers/BackendProvider";
1111
import {
12+
BACKEND_QUERY_KEY,
1213
DEFAULT_RATE_LIMIT_RPS,
1314
TWENTY_FOUR_HOURS_IN_MS,
1415
} from "@/utils/constants";
@@ -47,12 +48,12 @@ export const fetchPipelineRun = async (
4748
};
4849

4950
export const useFetchPipelineRunMetadata = (runId: string | undefined) => {
50-
const { backendUrl } = useBackend();
51+
const { backendUrl, configured } = useBackend();
5152

5253
return useQuery<PipelineRunResponse>({
53-
queryKey: ["pipeline-run-metadata", runId],
54+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-metadata", runId],
5455
queryFn: () => fetchPipelineRun(runId!, backendUrl),
55-
enabled: !!runId,
56+
enabled: !!runId && configured,
5657
refetchOnWindowFocus: false,
5758
staleTime: TWENTY_FOUR_HOURS_IN_MS,
5859
});
@@ -70,10 +71,12 @@ export const useFetchContainerExecutionState = (
7071
executionId: string | undefined,
7172
backendUrl: string,
7273
) => {
74+
const { configured } = useBackend();
75+
7376
return useQuery<GetContainerExecutionStateResponse>({
74-
queryKey: ["container-execution-state", executionId],
77+
queryKey: [BACKEND_QUERY_KEY, "container-execution-state", executionId],
7578
queryFn: () => fetchContainerExecutionState(executionId!, backendUrl),
76-
enabled: !!executionId,
79+
enabled: !!executionId && configured,
7780
refetchOnWindowFocus: false,
7881
});
7982
};

src/utils/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ export const ISO8601_DURATION_ZERO_DAYS = "P0D";
8080
export const DEFAULT_RATE_LIMIT_RPS = 10; // requests per second
8181

8282
export const MINUTES = 60 * 1000;
83+
84+
// Query key prefix for backend-dependent queries
85+
// All queries with this prefix are invalidated when the backend URL changes
86+
export const BACKEND_QUERY_KEY = "backend";

0 commit comments

Comments
 (0)