Skip to content

Commit ee33d9f

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 455e2ac commit ee33d9f

File tree

9 files changed

+106
-31
lines changed

9 files changed

+106
-31
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: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ describe("<RunDetails/>", () => {
6767
},
6868
});
6969

70+
const MOCK_BACKEND_URL = "http://localhost:8000";
71+
7072
const mockExecutionDetails: GetExecutionInfoResponse = {
7173
id: "test-execution-id",
7274
pipeline_run_id: "123",
@@ -160,7 +162,7 @@ describe("<RunDetails/>", () => {
160162
configured: true,
161163
available: true,
162164
ready: true,
163-
backendUrl: "http://localhost:8000",
165+
backendUrl: MOCK_BACKEND_URL,
164166
isConfiguredFromEnv: false,
165167
isConfiguredFromRelativePath: false,
166168
setEnvConfig: vi.fn(),
@@ -193,6 +195,50 @@ describe("<RunDetails/>", () => {
193195
});
194196
};
195197

198+
describe("Backend Configuration", () => {
199+
test("should render run details when backend is configured", async () => {
200+
// The default mock has configured: true and backendUrl: MOCK_BACKEND_URL
201+
// act
202+
renderWithProviders(<RunDetails />);
203+
204+
// assert
205+
await waitFor(() => {
206+
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
207+
expect(screen.getByText("Run Id:")).toBeInTheDocument();
208+
expect(screen.getByText("123")).toBeInTheDocument();
209+
});
210+
});
211+
212+
test("should render run details when backendUrl is empty string", async () => {
213+
// arrange - simulate custom backend toggle disabled (empty backendUrl)
214+
vi.mocked(useBackend).mockReturnValue({
215+
configured: true,
216+
available: true,
217+
ready: true,
218+
backendUrl: "",
219+
isConfiguredFromEnv: false,
220+
isConfiguredFromRelativePath: true,
221+
setEnvConfig: vi.fn(),
222+
setRelativePathConfig: vi.fn(),
223+
setBackendUrl: vi.fn(),
224+
ping: vi.fn(),
225+
});
226+
227+
// Query key no longer includes backendUrl - cache is shared regardless of URL
228+
// and invalidated when backend URL changes
229+
230+
// act
231+
renderWithProviders(<RunDetails />);
232+
233+
// assert
234+
await waitFor(() => {
235+
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
236+
expect(screen.getByText("Run Id:")).toBeInTheDocument();
237+
expect(screen.getByText("123")).toBeInTheDocument();
238+
});
239+
});
240+
});
241+
196242
describe("Inspect Pipeline Button", () => {
197243
test("should render inspect button when pipeline exists", async () => {
198244
// arrange

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
@@ -9,6 +9,7 @@ import { Spinner } from "@/components/ui/spinner";
99
import { useBackend } from "@/providers/BackendProvider";
1010
import type { RunStatus } from "@/types/pipelineRun";
1111
import { getBackendStatusString } from "@/utils/backend";
12+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1213

1314
const LogDisplay = ({
1415
logs,
@@ -114,10 +115,10 @@ const Logs = ({
114115
log_text?: string;
115116
system_error_exception_full?: string;
116117
}>();
117-
const { data, isLoading, error, refetch } = useQuery({
118-
queryKey: ["logs", executionId],
118+
const { data, isLoading, error } = useQuery({
119+
queryKey: [BACKEND_QUERY_KEY, "logs", executionId],
119120
queryFn: () => getLogs(String(executionId), backendUrl),
120-
enabled: isLogging,
121+
enabled: isLogging && configured,
121122
refetchInterval: 5000,
122123
refetchIntervalInBackground: false,
123124
});
@@ -142,10 +143,6 @@ const Logs = ({
142143
}
143144
}, [data, error]);
144145

145-
useEffect(() => {
146-
refetch();
147-
}, [backendUrl, refetch]);
148-
149146
if (!configured) {
150147
return (
151148
<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
@@ -10,11 +10,12 @@ import {
1010
getRunStatus,
1111
isStatusComplete,
1212
} from "@/services/executionService";
13+
import { BACKEND_QUERY_KEY } from "@/utils/constants";
1314

1415
const useRootExecutionId = (id: string) => {
15-
const { backendUrl } = useBackend();
16+
const { backendUrl, configured } = useBackend();
1617
const { data: rootExecutionId } = useQuery({
17-
queryKey: ["pipeline-run-execution-id", id],
18+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-execution-id", id],
1819
queryFn: async () => {
1920
const rootExecutionId = await fetchPipelineRun(id, backendUrl)
2021
.then((res) => res.root_execution_id)
@@ -27,7 +28,7 @@ const useRootExecutionId = (id: string) => {
2728
// assuming id is root_execution_id
2829
return id;
2930
},
30-
enabled: !!id && id.length > 0,
31+
enabled: !!id && id.length > 0 && configured,
3132
staleTime: Infinity,
3233
});
3334

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

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

4142
const rootExecutionId = useRootExecutionId(id);
4243

4344
const { data: executionDetails } = useQuery({
44-
enabled: !!rootExecutionId,
45-
queryKey: ["execution-details", rootExecutionId],
45+
enabled: !!rootExecutionId && configured,
46+
queryKey: [BACKEND_QUERY_KEY, "execution-details", rootExecutionId],
4647
queryFn: async () => {
4748
if (!rootExecutionId) {
4849
throw new Error("No root execution id found");
@@ -58,8 +59,8 @@ export const usePipelineRunData = (id: string) => {
5859
error,
5960
isLoading,
6061
} = useQuery({
61-
enabled: !!rootExecutionId && !!executionDetails,
62-
queryKey: ["pipeline-run", rootExecutionId],
62+
enabled: !!rootExecutionId && !!executionDetails && configured,
63+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run", rootExecutionId],
6364
queryFn: async () => {
6465
if (!rootExecutionId) {
6566
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
@@ -11,6 +11,7 @@ import type {
1111
import { useBackend } from "@/providers/BackendProvider";
1212
import type { RunStatus, TaskStatusCounts } from "@/types/pipelineRun";
1313
import {
14+
BACKEND_QUERY_KEY,
1415
DEFAULT_RATE_LIMIT_RPS,
1516
TWENTY_FOUR_HOURS_IN_MS,
1617
} from "@/utils/constants";
@@ -45,12 +46,12 @@ export const fetchPipelineRun = async (
4546
};
4647

4748
export const useFetchPipelineRunMetadata = (runId: string | undefined) => {
48-
const { backendUrl } = useBackend();
49+
const { backendUrl, configured } = useBackend();
4950

5051
return useQuery<PipelineRunResponse>({
51-
queryKey: ["pipeline-run-metadata", runId],
52+
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-metadata", runId],
5253
queryFn: () => fetchPipelineRun(runId!, backendUrl),
53-
enabled: !!runId,
54+
enabled: !!runId && configured,
5455
refetchOnWindowFocus: false,
5556
staleTime: TWENTY_FOUR_HOURS_IN_MS,
5657
});
@@ -68,10 +69,12 @@ export const useFetchContainerExecutionState = (
6869
executionId: string | undefined,
6970
backendUrl: string,
7071
) => {
72+
const { configured } = useBackend();
73+
7174
return useQuery<GetContainerExecutionStateResponse>({
72-
queryKey: ["container-execution-state", executionId],
75+
queryKey: [BACKEND_QUERY_KEY, "container-execution-state", executionId],
7376
queryFn: () => fetchContainerExecutionState(executionId!, backendUrl),
74-
enabled: !!executionId,
77+
enabled: !!executionId && configured,
7578
refetchOnWindowFocus: false,
7679
});
7780
};

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)