From d9210b6e86d02b9e213124e208432faf86ef56c9 Mon Sep 17 00:00:00 2001 From: jean-baptiste Date: Thu, 4 Jun 2026 17:57:07 +0200 Subject: [PATCH] polish(reconcile): throttle back-to-back sweeps, document the sync call site Review follow-ups: loadProjects() fires get-projects twice per sidebar paint (showArchived false/true), running the reconcile sweep back-to-back; a 1s throttle skips the redundant second pass while the live watcher covers anything landing inside the window. Also note at the call site that the reconcile is synchronous, so the missing await next to the cold-start branch's 'await populateCacheViaWorker()' is intentional. --- main.js | 2 ++ session-cache.js | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/main.js b/main.js index 43111d87..0467b9d3 100644 --- a/main.js +++ b/main.js @@ -648,6 +648,8 @@ ipcMain.handle('get-projects', async (_event, showArchived) => { // Cache already populated: pick up folders changed while the app was // closed, or never indexed by an older build, so sessions/worktrees don't // silently go missing. Stat-gated, so it's cheap when nothing has changed. + // Synchronous (readdir/stat sweep) — completes before buildProjectsFromCache + // below; no await needed despite the await in the cold-start branch above. reconcileCacheFromFilesystem(); } diff --git a/session-cache.js b/session-cache.js index ccaef66a..e6f9f4ad 100644 --- a/session-cache.js +++ b/session-cache.js @@ -270,8 +270,19 @@ function refreshFolder(folder, opts = {}) { * app was closed, or that predates the build which first indexed it, is * otherwise never picked up, because the cold-start full scan * (populateCacheViaWorker) only runs when the cache is completely empty. + * + * Throttled: the renderer's loadProjects() fires get-projects twice per sidebar + * paint (showArchived false/true via Promise.all), which would run the sweep + * back-to-back. The second pass is idempotent but wasted work — and skipping it + * keeps a single metaMap snapshot per paint. Changes landing inside the + * throttle window are still picked up by the live watcher. */ +const RECONCILE_THROTTLE_MS = 1000; +let lastReconcileAt = 0; function reconcileCacheFromFilesystem() { + const now = Date.now(); + if (now - lastReconcileAt < RECONCILE_THROTTLE_MS) return; + lastReconcileAt = now; try { const metaMap = getAllFolderMeta(); const folders = fs.readdirSync(PROJECTS_DIR, { withFileTypes: true })