From 86817a24fc01469a50ee7a437574dcfd3c7dc782 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 17 Oct 2026 22:06:09 +0700 Subject: [PATCH] perf(app): fetch project and workspace directories concurrently (#7242) --- .../src/runtime/directory-sync/index.test.ts | 54 +++++++++++++++++++ .../app/src/runtime/directory-sync/index.ts | 28 ++++++---- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/packages/app/src/runtime/directory-sync/index.test.ts b/packages/app/src/runtime/directory-sync/index.test.ts index 2dea552239..98e0ea29b3 100644 --- a/packages/app/src/runtime/directory-sync/index.test.ts +++ b/packages/app/src/runtime/directory-sync/index.test.ts @@ -1207,6 +1207,60 @@ describe("DirectorySync session readiness", () => { directory.dispose(); }); + it.each(["projects", "workspaces"] as const)( + "requests projects and workspaces together and commits once when %s responds first", + async (firstResponse) => { + const serverId = `parallel-directory-${firstResponse}`; + const { client, directory } = createDirectory(serverId); + const store = useSessionStore.getState(); + store.initializeSession(serverId, client as unknown as DaemonClient, 1); + store.updateSessionServerInfo(serverId, { + serverId, + hostname: null, + version: "test", + features: { workspaceMultiplicity: true, projectList: true }, + }); + const releaseProjects = client.holdProjectFetch(); + const releaseWorkspaces = client.holdWorkspaceFetch(); + const project = { + projectId: "parallel-project", + projectDisplayName: "Project baseline", + projectRootPath: "/repo/parallel", + projectKind: "git" as const, + }; + const projects: ProjectListResult = { requestId: "projects", projects: [project] }; + const workspaces: WorkspaceFetchResult = { + requestId: "workspaces", + entries: [createWorkspaceEntry("parallel-workspace")], + emptyProjects: [{ ...project, projectDisplayName: "Newer workspace overlay" }], + pageInfo: { hasMore: false, nextCursor: null, prevCursor: null }, + }; + const refresh = directory.refreshWorkspaces(); + try { + await expect.poll(() => client.listProjectsCalls).toBe(1); + await expect.poll(() => client.fetchWorkspacesCalls).toBe(1); + if (firstResponse === "projects") releaseProjects(projects); + else releaseWorkspaces(workspaces); + await new Promise((resolve) => setTimeout(resolve, 0)); + expect(useSessionStore.getState().sessions[serverId].workspaces.size).toBe(0); + expect(useSessionStore.getState().sessions[serverId].projects.size).toBe(0); + releaseProjects(projects); + releaseWorkspaces(workspaces); + await refresh; + const session = useSessionStore.getState().sessions[serverId]; + expect(session.workspaces.has("parallel-workspace")).toBe(true); + expect(session.projects.get(project.projectId)?.projectDisplayName).toBe( + "Newer workspace overlay", + ); + } finally { + releaseProjects(projects); + releaseWorkspaces(workspaces); + await refresh; + directory.dispose(); + } + }, + ); + it("keeps the accepted baseline when an old connection finishes its workspace request", async () => { const serverId = "superseded-workspace-baseline"; const { client, directory } = createDirectory(serverId); diff --git a/packages/app/src/runtime/directory-sync/index.ts b/packages/app/src/runtime/directory-sync/index.ts index 28b217fea6..b59440d782 100644 --- a/packages/app/src/runtime/directory-sync/index.ts +++ b/packages/app/src/runtime/directory-sync/index.ts @@ -563,16 +563,20 @@ export class DirectorySync { const serverInfo = useSessionStore.getState().sessions[this.serverId]?.serverInfo; const supportsProjectList = serverInfo?.features?.projectList === true; const supportsDirectorySync = serverInfo?.features?.directorySync === true; - if (supportsProjectList) { - await this.fetchProjectSnapshot(client, source, transaction, supportsDirectorySync); - } - await this.fetchWorkspaceSnapshot( - client, - source, - transaction, - input?.subscribe === true, - supportsDirectorySync, - ); + const projectsReady = supportsProjectList + ? this.fetchProjectSnapshot(client, source, transaction, supportsDirectorySync) + : Promise.resolve(); + await Promise.all([ + projectsReady, + this.fetchWorkspaceSnapshot( + client, + source, + transaction, + input?.subscribe === true, + supportsDirectorySync, + projectsReady, + ), + ]); if (!supportsProjectList) { this.buildLegacyProjectSnapshot(transaction.snapshot); (transaction.snapshot.syncModes ??= {}).projects = @@ -592,6 +596,7 @@ export class DirectorySync { transaction: DirectoryTransaction, initialSubscribe: boolean, supportsDirectorySync: boolean, + projectsReady: Promise, ): Promise { let cursor: string | null = null; let subscribe = initialSubscribe; @@ -618,6 +623,9 @@ export class DirectorySync { }); payload = await subscription.ready; } else payload = await client.fetchWorkspaces(query); + // Fetch both directories together, but apply the project baseline before the + // workspace page's empty-project overlays, regardless of response order. + await projectsReady; this.assertWorkspaceTransactionCurrent(client, source, transaction); applyWorkspaceSnapshotPage(transaction.snapshot, payload, cursor === null); if (!payload.pageInfo.hasMore || !payload.pageInfo.nextCursor) return;