perf(dashboard): cache gh CLI checks and defer SQLite integrity scan
Cold-start dashboard responsiveness went from ~99s to ~6-11s. CPU profiling identified two synchronous-spawn hotspots blocking the event loop: - `GitHubTrackingReconciler` scanned up to 200 done tasks per startup, each call into `getIssue` invoking `isGhAvailable()` + `isGhAuthenticated()` via `execFileSync`. `gh auth status` makes a network roundtrip, so 400 sync spawns ≈ 71s of pure event-loop blocking (69% of cold-start CPU). Memoized both checks with a 60s TTL; `resetGhAvailabilityCache()` is exported for login/logout flows that need immediate invalidation. - `PRAGMA integrity_check(100)` walks every page of the SQLite file (~7s per database, multiple DBs × projects). The deferred check was scheduled 3s after init — right in the responsiveness-critical window. Pushed to 60s so the user is already interacting before it runs; check itself is unchanged. Also yields the event loop between major InProcessRuntime init phases and between self-healing recovery steps (34 per project), defers orphan-task AI agent resumption by 30s (env-overridable, auto-zero under Vitest), and ships an opt-in `FUSION_TRACE_EL_LAG=/path/to/file` event-loop lag tracer that diagnosed all of the above. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2180,6 +2180,31 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
});
|
||||
}
|
||||
|
||||
// ── Event-loop lag tracer (debug aid) ──
|
||||
// Polls every 50ms and logs whenever the loop was blocked by >150ms since
|
||||
// the previous tick. Pinpoints which synchronous operation is hogging the
|
||||
// event loop during startup. Disabled unless FUSION_TRACE_EL_LAG is set
|
||||
// to a file path (writes to that file with raw timestamps so log output
|
||||
// doesn't pollute the analysis).
|
||||
if (process.env.FUSION_TRACE_EL_LAG) {
|
||||
const lagPath = process.env.FUSION_TRACE_EL_LAG;
|
||||
const fs = await import("node:fs");
|
||||
const lagStream = fs.createWriteStream(lagPath, { flags: "w" });
|
||||
const LAG_THRESHOLD_MS = 150;
|
||||
const POLL_MS = 50;
|
||||
const traceStart = performance.now();
|
||||
let last = traceStart;
|
||||
setInterval(() => {
|
||||
const now = performance.now();
|
||||
const delta = now - last - POLL_MS;
|
||||
last = now;
|
||||
if (delta > LAG_THRESHOLD_MS) {
|
||||
const tSinceStart = Math.round(now - traceStart);
|
||||
lagStream.write(`t+${tSinceStart}ms: blocked ${Math.round(delta)}ms\n`);
|
||||
}
|
||||
}, POLL_MS).unref();
|
||||
}
|
||||
|
||||
const server = app.listen(selectedPort, selectedHost);
|
||||
|
||||
server.on("error", (err: NodeJS.ErrnoException) => {
|
||||
|
||||
Reference in New Issue
Block a user