Skip to content

Commit 77def07

Browse files
committed
fix(frontend): ignore idle/checking in the desktop cache reset
Aikido flagged the offline check as contradicting its own comment. It does, and the consequence is worse than the mismatch: the monitor has four states, and treating everything non-offline as reachable inverted the recovery path. start() sets "checking" and stop() sets "idle". So when the server is down and the monitor restarts, offline -> checking read as a flip and reset the cache while routing still pointed at the local fallback, then checking -> online read as no flip and skipped the reset that mattered. The local fallback's answers stayed cached after the server came back. Now only online and offline are considered. Two tests added, both verified to fail against the previous logic.
1 parent df6e902 commit 77def07

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

frontend/editor/src/desktop/components/DesktopQueryCacheReset.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,38 @@ describe("DesktopQueryCacheReset", () => {
9797

9898
expect(queryFn).toHaveBeenCalledTimes(1);
9999
});
100+
101+
it("resets on recovery, not on the checking state in between", async () => {
102+
const queryFn = vi.fn<() => Promise<string>>().mockResolvedValue("x");
103+
104+
renderWithConsumer(queryFn);
105+
await waitFor(() => expect(queryFn).toHaveBeenCalledTimes(1));
106+
107+
const emit = (status: string) =>
108+
serverListeners.forEach((l) => l({ status }));
109+
110+
emit("offline");
111+
await waitFor(() => expect(queryFn).toHaveBeenCalledTimes(2));
112+
113+
// A monitor restart while still down must not read as recovery — otherwise
114+
// the reset lands here and the real online transition is skipped.
115+
emit("checking");
116+
expect(queryFn).toHaveBeenCalledTimes(2);
117+
118+
emit("online");
119+
await waitFor(() => expect(queryFn).toHaveBeenCalledTimes(3));
120+
});
121+
122+
it("does not reset when the monitor stops while offline", async () => {
123+
const queryFn = vi.fn<() => Promise<string>>().mockResolvedValue("cached");
124+
125+
renderWithConsumer(queryFn);
126+
await waitFor(() => expect(queryFn).toHaveBeenCalledTimes(1));
127+
128+
serverListeners.forEach((l) => l({ status: "offline" }));
129+
await waitFor(() => expect(queryFn).toHaveBeenCalledTimes(2));
130+
131+
serverListeners.forEach((l) => l({ status: "idle" }));
132+
expect(queryFn).toHaveBeenCalledTimes(2);
133+
});
100134
});

frontend/editor/src/desktop/components/DesktopQueryCacheReset.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ export function DesktopQueryCacheReset() {
1717
const reset = () => void queryClient.resetQueries();
1818
const unsubscribeMode = connectionModeService.subscribeToModeChanges(reset);
1919

20-
// Ignore idle/checking, and the state replayed on subscribe.
20+
// idle/checking say nothing about reachability — treating them as "not
21+
// offline" would reset on offline→checking and then skip the real recovery.
2122
let wasOffline: boolean | null = null;
2223
const unsubscribeServer = selfHostedServerMonitor.subscribe(
2324
({ status }) => {
25+
if (status !== "online" && status !== "offline") return;
2426
const isOffline = status === "offline";
2527
if (wasOffline !== null && wasOffline !== isOffline) reset();
2628
wasOffline = isOffline;

0 commit comments

Comments
 (0)