Reuse git facts for workspace observation

This commit is contained in:
Mohamed Boudra
2026-05-27 20:33:21 +07:00
parent 5d8dc800fc
commit fa1b3e88f0
4 changed files with 163 additions and 22 deletions
@@ -70,14 +70,17 @@ The frozen home used for the comparison contained 22 workspaces.
### Before/After
| run | code shape | client shape | git commands | failures | elapsed |
| ----------- | ------------- | ----------------------------------------- | -----------: | -------: | ------: |
| baseline | before change | legacy sidebar PR fanout | 529 | 20 | 39039ms |
| split check | after change | legacy sidebar PR fanout | 375 | 15 | 39039ms |
| after | after change | snapshot-only sidebar, no PR badge fanout | 372 | 15 | 31273ms |
| run | code shape | client shape | git commands | failures | elapsed |
| ----------- | -------------------------- | ----------------------------------------- | -----------: | -------: | ------: |
| baseline | before change | legacy sidebar PR fanout | 529 | 20 | 39039ms |
| split check | after change | legacy sidebar PR fanout | 375 | 15 | 39039ms |
| after | after change | snapshot-only sidebar, no PR badge fanout | 372 | 15 | 31273ms |
| after 2 | after service fact sharing | snapshot-only sidebar, no PR badge fanout | 308 | 15 | 31334ms |
The server-side fact reuse accounts for nearly all measured git command reduction: `529 -> 375` (`-154`, `-29.1%`) even when the old PR fanout is still forced. Removing the sidebar fanout removes the ad hoc request path, but in this run it only changed command count by `3` because the refreshed workspace snapshots already carried the PR data by the time the fanout ran.
The second pass shares checkout facts between workspace observation setup and snapshot refresh. That removes another `64` git commands from the same frozen-home run: `372 -> 308` (`-17.2%` from the previous after, `-41.8%` from baseline).
### Baseline: before change + legacy PR fanout
```json
@@ -148,6 +151,41 @@ The server-side fact reuse accounts for nearly all measured git command reductio
}
```
### After 2: shared service-level facts
```json
{
"scenario": "snapshotOnly",
"workspaceCount": 22,
"elapsedMs": 31334,
"git": {
"total": 308,
"failed": 15,
"maxConcurrent": 8,
"byCommand": [
{ "key": "show-ref --verify --quiet refs/heads/main", "count": 31 },
{ "key": "rev-parse --git-common-dir", "count": 26 },
{ "key": "show-ref --verify --quiet refs/remotes/origin/main", "count": 22 },
{ "key": "ls-files --others --exclude-standard", "count": 18 },
{ "key": "status --porcelain", "count": 18 },
{ "key": "merge-base HEAD origin/main", "count": 17 },
{ "key": "config --get remote.origin.url", "count": 13 },
{ "key": "rev-parse --abbrev-ref HEAD", "count": 13 },
{ "key": "rev-parse --absolute-git-dir", "count": 13 },
{ "key": "rev-parse --show-toplevel", "count": 13 },
{ "key": "symbolic-ref --quiet refs/remotes/origin/HEAD", "count": 13 },
{ "key": "fetch origin --prune", "count": 5 }
]
},
"process": {
"cpuUserMs": 1817,
"cpuSystemMs": 1869,
"rssDeltaMb": 16.7,
"heapUsedDeltaMb": 10.4
}
}
```
## Snapshot Equivalence Guard
Added a focused utility test proving that status, shortstat, and PR status return the same data when run from shared snapshot facts. The same test records git calls and asserts the facts-backed path does not re-run:
@@ -165,9 +203,9 @@ This pass reshaped the data flow and removed the sidebar PR badge special path.
The benchmark still shows repeated per-workspace reads that are candidates for the next pass:
- origin URL lookup repeats across snapshot facts and GitHub remote resolution paths.
- base ref existence checks still repeat as `show-ref` probes.
- repo/worktree identity still requires one root/current-branch rev-parse per workspace.
- default branch resolution still repeats `symbolic-ref refs/remotes/origin/HEAD`.
- repo common-dir lookup is lower, but still above the apparent git workspace count.
- shortstat still runs its own merge-base/diff/untracked scan per workspace.
The important invariant now is clearer: sidebar-visible git data should flow from `WorkspaceGitService` snapshots, and snapshot builders should receive reusable git facts through `CheckoutContext`.
@@ -112,6 +112,8 @@ function createCheckoutSnapshotFacts(cwd: string): CheckoutSnapshotFacts {
worktreeRoot: cwd,
currentBranch: "main",
remoteUrl: "https://github.com/acme/repo.git",
absoluteGitDir: join(cwd, ".git"),
gitCommonDir: join(cwd, ".git"),
paseoWorktree: { isPaseoOwnedWorktree: false },
storedBaseRef: null,
resolvedBaseRef: "main",
@@ -427,10 +429,12 @@ describe("WorkspaceGitServiceImpl", () => {
test("multiple listeners on the same workspace share one observation setup", async () => {
const getPullRequestStatus = vi.fn(async () => createPullRequestStatusResult());
const getCheckoutSnapshotFacts = vi.fn(async (cwd: string) => createCheckoutSnapshotFacts(cwd));
const resolveAbsoluteGitDir = vi.fn(async () => join(REPO_CWD, ".git"));
let nowMs = Date.parse("2026-04-12T00:00:00.000Z");
const service = createService({
getCheckoutSnapshotFacts,
getPullRequestStatus,
resolveAbsoluteGitDir,
now: () => new Date(nowMs),
@@ -441,7 +445,8 @@ describe("WorkspaceGitServiceImpl", () => {
await flushPromises();
expect(getPullRequestStatus).toHaveBeenCalledTimes(0);
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(1);
expect(getCheckoutSnapshotFacts).toHaveBeenCalledTimes(1);
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(0);
first.unsubscribe();
second.unsubscribe();
@@ -473,7 +478,7 @@ describe("WorkspaceGitServiceImpl", () => {
);
expect(getPullRequestStatus).toHaveBeenCalledTimes(1);
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(1);
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(0);
subscription.unsubscribe();
service.dispose();
@@ -482,9 +487,15 @@ describe("WorkspaceGitServiceImpl", () => {
test("repo-level fetch intervals are shared for workspaces in the same repo", async () => {
const runGitFetch = vi.fn(async () => {});
const hasOriginRemote = vi.fn(async () => true);
const getCheckoutSnapshotFacts = vi.fn(async (cwd: string) => ({
...createCheckoutSnapshotFacts(cwd),
gitCommonDir: join(REPO_CWD, ".git"),
absoluteGitDir: join(REPO_CWD, ".git"),
}));
const resolveAbsoluteGitDir = vi.fn(async () => join(REPO_CWD, ".git"));
const service = createService({
getCheckoutSnapshotFacts,
resolveAbsoluteGitDir,
hasOriginRemote,
runGitFetch,
@@ -496,11 +507,12 @@ describe("WorkspaceGitServiceImpl", () => {
vi.fn(),
);
await vi.waitFor(() => {
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(2);
expect(getCheckoutSnapshotFacts).toHaveBeenCalledTimes(2);
expect(runGitFetch).toHaveBeenCalledTimes(1);
});
expect(hasOriginRemote).toHaveBeenCalledTimes(1);
expect(resolveAbsoluteGitDir).toHaveBeenCalledTimes(0);
expect(hasOriginRemote).toHaveBeenCalledTimes(0);
expect(runGitFetch).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(180_000);
@@ -47,6 +47,7 @@ const WORKING_TREE_WATCH_FALLBACK_REFRESH_MS = 5_000;
const WORKSPACE_GIT_AUXILIARY_READ_TTL_MS = 15_000;
// Non-forced refresh triggers share this minimum gap to absorb watcher/self-heal bursts; force bypasses it.
const WORKSPACE_GIT_INTERNAL_MIN_GAP_MS = 2_000;
const WORKSPACE_GIT_FACTS_REUSE_TTL_MS = 1_000;
const LINUX_WATCH_MAX_DIRS = 5_000;
const LINUX_WATCH_REFRESH_COOLDOWN_MS = 2_000;
const LINUX_WATCH_IGNORE_TTL_MS = 5 * 60 * 1_000;
@@ -271,6 +272,9 @@ interface WorkspaceGitTarget {
latestGithubLoadedAtMs: number | null;
latestSnapshot: WorkspaceGitRuntimeSnapshot | null;
latestSnapshotLoadedAtMs: number | null;
latestFacts: CheckoutSnapshotFacts | null;
latestFactsLoadedAtMs: number | null;
factsPromise: Promise<CheckoutSnapshotFacts> | null;
latestFingerprint: string | null;
lastShellOutAtMs: number | null;
repoGitRoot: string | null;
@@ -787,6 +791,9 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
latestGithubLoadedAtMs: null,
latestSnapshot: null,
latestSnapshotLoadedAtMs: null,
latestFacts: null,
latestFactsLoadedAtMs: null,
factsPromise: null,
latestFingerprint: null,
lastShellOutAtMs: null,
repoGitRoot: null,
@@ -837,7 +844,8 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
}
private async setupWorkspaceObservation(target: WorkspaceGitTarget): Promise<void> {
const gitDir = await this.deps.resolveAbsoluteGitDir(target.cwd);
const facts = await this.getFactsForObservation(target);
const gitDir = facts?.isGit ? facts.absoluteGitDir : null;
if (!this.isActiveObservedWorkspaceTarget(target)) {
return;
}
@@ -846,7 +854,10 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
return;
}
const repoGitRoot = await this.resolveWorkspaceGitRefsRoot(gitDir);
const repoGitRoot =
facts?.isGit && facts.gitCommonDir
? facts.gitCommonDir
: await this.resolveWorkspaceGitRefsRoot(gitDir);
if (!this.isActiveObservedWorkspaceTarget(target)) {
return;
}
@@ -858,6 +869,48 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
}
}
private async getFactsForObservation(
target: WorkspaceGitTarget,
): Promise<CheckoutSnapshotFacts | null> {
return this.loadCheckoutFacts(target, {
paseoHome: this.paseoHome,
logger: this.logger,
allowRecent: true,
});
}
private loadCheckoutFacts(
target: WorkspaceGitTarget,
options: CheckoutContext & { allowRecent: boolean },
): Promise<CheckoutSnapshotFacts> {
if (options.allowRecent && target.latestFacts && target.latestFactsLoadedAtMs !== null) {
const ageMs = this.deps.now().getTime() - target.latestFactsLoadedAtMs;
if (ageMs < WORKSPACE_GIT_FACTS_REUSE_TTL_MS) {
return Promise.resolve(target.latestFacts);
}
}
if (target.factsPromise) {
return target.factsPromise;
}
const { allowRecent: _allowRecent, ...context } = options;
const promise = this.deps
.getCheckoutSnapshotFacts(target.cwd, context)
.then((facts) => {
target.latestFacts = facts;
target.latestFactsLoadedAtMs = this.deps.now().getTime();
return facts;
})
.finally(() => {
if (target.factsPromise === promise) {
target.factsPromise = null;
}
});
target.factsPromise = promise;
return promise;
}
private isActiveObservedWorkspaceTarget(target: WorkspaceGitTarget): boolean {
return (
!target.closed &&
@@ -996,7 +1049,11 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
return;
}
const hasOrigin = await this.deps.hasOriginRemote(workspaceTarget.cwd);
const facts = workspaceTarget.latestFacts;
const hasOrigin =
facts?.isGit === true
? facts.remoteUrl !== null
: await this.deps.hasOriginRemote(workspaceTarget.cwd);
if (!this.isActiveObservedWorkspaceTarget(workspaceTarget)) {
return;
}
@@ -1505,7 +1562,10 @@ export class WorkspaceGitServiceImpl implements WorkspaceGitService {
const cwd = target.cwd;
const previousGitHubPollKey = this.getGitHubPollKey(target);
const baseContext: CheckoutContext = { paseoHome: this.paseoHome, logger: this.logger };
const facts = await this.deps.getCheckoutSnapshotFacts(cwd, baseContext);
const facts = await this.loadCheckoutFacts(target, {
...baseContext,
allowRecent: !request.force,
});
const context: CheckoutContext = { ...baseContext, facts };
const checkoutStatus = await this.deps.getCheckoutStatus(cwd, context);
if (!checkoutStatus.isGit) {
+38 -7
View File
@@ -735,6 +735,8 @@ export type CheckoutSnapshotFacts =
worktreeRoot: string;
currentBranch: string | null;
remoteUrl: string | null;
absoluteGitDir: string | null;
gitCommonDir: string | null;
paseoWorktree: PaseoWorktreeForCwd;
storedBaseRef: string | null;
resolvedBaseRef: string | null;
@@ -817,7 +819,13 @@ export async function getMainRepoRoot(cwd: string): Promise<string> {
cwd,
envOverlay: READ_ONLY_GIT_ENV,
});
const commonDir = resolveGitRevParsePath(cwd, commonDirOut);
return getMainRepoRootFromCommonDir(cwd, resolveGitRevParsePath(cwd, commonDirOut));
}
async function getMainRepoRootFromCommonDir(
cwd: string,
commonDir: string | null,
): Promise<string> {
if (!commonDir) {
throw new Error("Not in a git repository");
}
@@ -1101,6 +1109,18 @@ export async function resolveAbsoluteGitDir(cwd: string): Promise<string | null>
}
}
async function resolveGitCommonDir(cwd: string): Promise<string | null> {
try {
const { stdout } = await runGitCommand(["rev-parse", "--git-common-dir"], {
cwd,
envOverlay: READ_ONLY_GIT_ENV,
});
return resolveGitRevParsePath(cwd, stdout);
} catch {
return null;
}
}
async function abortGitPullConflictState(cwd: string): Promise<void> {
const gitDir = await resolveAbsoluteGitDir(cwd);
if (!gitDir) {
@@ -1389,6 +1409,8 @@ interface CheckoutInspectionContext {
worktreeRoot: string;
currentBranch: string | null;
remoteUrl: string | null;
absoluteGitDir: string | null;
gitCommonDir: string | null;
paseoWorktree: PaseoWorktreeForCwd;
}
@@ -1402,16 +1424,21 @@ async function inspectCheckoutContext(
return null;
}
const [currentBranch, remoteUrl, paseoWorktree] = await Promise.all([
getCurrentBranch(cwd),
getOriginRemoteUrl(cwd),
getPaseoWorktreeForCwd(cwd, context, root),
]);
const [currentBranch, remoteUrl, absoluteGitDir, gitCommonDir, paseoWorktree] =
await Promise.all([
getCurrentBranch(cwd),
getOriginRemoteUrl(cwd),
resolveAbsoluteGitDir(cwd),
resolveGitCommonDir(cwd),
getPaseoWorktreeForCwd(cwd, context, root),
]);
return {
worktreeRoot: root,
currentBranch,
remoteUrl,
absoluteGitDir,
gitCommonDir,
paseoWorktree,
};
} catch (error) {
@@ -1464,7 +1491,9 @@ export async function getCheckoutSnapshotFacts(
? readPaseoWorktreeBaseRef(inspected.paseoWorktree.worktreeRoot)
: null;
const resolvedBaseRef = storedBaseRef ?? (await resolveBaseRef(cwd));
const mainRepoRoot = await getMainRepoRoot(cwd).catch(() => null);
const mainRepoRoot = await getMainRepoRootFromCommonDir(cwd, inspected.gitCommonDir).catch(
() => null,
);
let comparisonBaseRef: string | null = null;
if (
resolvedBaseRef &&
@@ -1512,6 +1541,8 @@ export async function getCheckoutSnapshotFacts(
worktreeRoot: inspected.worktreeRoot,
currentBranch: inspected.currentBranch,
remoteUrl: inspected.remoteUrl,
absoluteGitDir: inspected.absoluteGitDir,
gitCommonDir: inspected.gitCommonDir,
paseoWorktree: inspected.paseoWorktree,
storedBaseRef,
resolvedBaseRef,