From d6052cff8b6aa88cc4a6097b3ae6b01b2c6447db Mon Sep 17 00:00:00 2001 From: zipg Date: Fri, 26 Jun 2026 12:32:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=81=BF=E5=85=8D=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=B0=9D=E8=AF=95=E6=AE=8B=E7=95=99=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: staff --- .../__tests__/connectionStore.mq.spec.ts | 32 +++++++++++++++++++ apps/desktop/src/stores/connectionStore.ts | 25 ++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/stores/__tests__/connectionStore.mq.spec.ts b/apps/desktop/src/stores/__tests__/connectionStore.mq.spec.ts index e3f32d23df..c8e1b36dfc 100644 --- a/apps/desktop/src/stores/__tests__/connectionStore.mq.spec.ts +++ b/apps/desktop/src/stores/__tests__/connectionStore.mq.spec.ts @@ -83,6 +83,38 @@ describe("connectionStore MQ sidebar tree", () => { expect(node.isExpanded).toBe(true); }); + it("reuses an in-flight connection attempt instead of recording stale superseded errors", async () => { + let resolveConnect: ((value: string) => void) | undefined; + const connectDb = vi.fn( + () => + new Promise((resolve) => { + resolveConnect = resolve; + }), + ); + + vi.doMock("@/lib/tauriRuntime", () => ({ isTauriRuntime: () => false })); + vi.doMock("@/lib/api", () => ({ + checkConnectionHealth: vi.fn().mockResolvedValue(undefined), + connectDb, + })); + + const { useConnectionStore } = await import("@/stores/connectionStore"); + const store = useConnectionStore(); + const connection = mqConnection(); + store.connections = [connection]; + + const first = store.ensureConnected(connection.id); + const second = store.ensureConnected(connection.id); + await Promise.resolve(); + + expect(connectDb).toHaveBeenCalledTimes(1); + resolveConnect?.(connection.id); + await Promise.all([first, second]); + + expect(store.connectedIds.has(connection.id)).toBe(true); + expect(store.connectionErrors[connection.id]).toBeUndefined(); + }); + it("stores the selected tenant when opening an MQ admin tab", async () => { const { useConnectionStore } = await import("@/stores/connectionStore"); const { useQueryStore } = await import("@/stores/queryStore"); diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index 57573fee41..638b6eef01 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -63,6 +63,7 @@ const METADATA_LOAD_DISABLED_QUERY_TIMEOUT_MS = 60_000; const DISCONNECT_REQUEST_TIMEOUT_MS = 5_000; const MONGO_LEGACY_DRIVER_PROFILE = "mongodb-legacy"; const MONGO_LEGACY_DRIVER_LABEL = "MongoDB (Legacy)"; +const SUPERSEDED_CONNECTION_ATTEMPT_MESSAGE = "Connection attempt was superseded by a newer attempt"; function sidebarObjectGroupPageSize(): number { const settingsStore = useSettingsStore(); const size = settingsStore.desktopSettings.sidebar_table_page_size; @@ -224,6 +225,7 @@ export const useConnectionStore = defineStore("connection", () => { const sidebarLayout = ref(emptyLayout()); let layoutPersistTimer: ReturnType | null = null; const staleTreeRefreshIds = new Set(); + const connectInFlight = new Map>(); let beforeConnectHandler: BeforeConnectHandler | null = null; let initFromDiskPromise: Promise | null = null; @@ -255,6 +257,10 @@ export const useConnectionStore = defineStore("connection", () => { return String(error); } + function isSupersededConnectionAttempt(error: unknown): boolean { + return connectionErrorMessage(error).includes(SUPERSEDED_CONNECTION_ATTEMPT_MESSAGE); + } + function setConnectionError(connectionId: string, message: string) { connectionErrors.value[connectionId] = message; } @@ -1196,7 +1202,12 @@ export const useConnectionStore = defineStore("connection", () => { recordConnectionError(connectionId, error); throw error; } - try { + const existingConnect = connectInFlight.get(connectionId); + if (existingConnect) { + await existingConnect; + return; + } + const connectPromise = (async () => { await beforeConnectHandler?.(config); await withConnectionAttemptTimeout(api.connectDb(config), config); await syncMongoLegacyDriverFallback(connectionId, config); @@ -1204,9 +1215,21 @@ export const useConnectionStore = defineStore("connection", () => { markConnectionHealthChecked(connectionId); activeConnectionId.value = connectionId; clearConnectionError(connectionId); + })(); + connectInFlight.set(connectionId, connectPromise); + try { + await connectPromise; } catch (e) { + if (isSupersededConnectionAttempt(e) && connectedIds.value.has(connectionId)) { + clearConnectionError(connectionId); + return; + } recordConnectionError(connectionId, e); throw e; + } finally { + if (connectInFlight.get(connectionId) === connectPromise) { + connectInFlight.delete(connectionId); + } } }