perf(renderer): index detectedWorktreesByRepo so SSH panes and cards stop walking the full detected catalog on every store write (#12421)

* perf(renderer): index detected worktrees for owner lookups

* test(renderer): make detected index perf gate deterministic

* test(renderer): cover detected index owner hot path
This commit is contained in:
Brennan Benson 2026-08-04 00:26:40 -07:00 committed by GitHub
parent 9f638da62d
commit c498d2d405
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 451 additions and 33 deletions

View File

@ -7,7 +7,11 @@ import {
import { parseWorkspaceKey } from '../../../shared/workspace-scope'
import { getRepoIdFromWorktreeId } from '@/store/slices/worktree-helpers'
import { addRoute, resolveExactWorktreeRoute, routeForOwner } from './worktree-owner-route'
import { resolveIndexedWorktreeOwner } from './worktree-runtime-owner-index'
import {
findIndexedDetectedWorktrees,
hasIndexedDetectedWorktree,
resolveIndexedWorktreeOwner
} from './worktree-runtime-owner-index'
import {
findFolderWorkspaceOwner,
getExecutionHostIdForFolderWorkspace,
@ -51,11 +55,7 @@ function ownerRecordsOnHost(
executionHostId: ExecutionHostId
): WorktreeOperationOwnerRecord[] {
const owners: WorktreeOperationOwnerRecord[] = []
const catalogs = [
...Object.values(state.worktreesByRepo ?? {}),
...Object.values(state.detectedWorktreesByRepo ?? {}).map((result) => result.worktrees)
]
for (const worktrees of catalogs) {
for (const worktrees of Object.values(state.worktreesByRepo ?? {})) {
for (const worktree of worktrees) {
if (
worktree.id === worktreeId &&
@ -65,6 +65,11 @@ function ownerRecordsOnHost(
}
}
}
for (const worktree of findIndexedDetectedWorktrees(state.detectedWorktreesByRepo, worktreeId)) {
if (parseExecutionHostId(worktree.hostId)?.id === executionHostId) {
owners.push(worktree)
}
}
return owners
}
@ -135,12 +140,8 @@ export function resolveWorktreeOperationRouteResult(
}
const hasKnownWorktree =
Object.values(state.worktreesByRepo ?? {}).some((worktrees) =>
worktrees.some((worktree) => worktree.id === worktreeId)
) ||
Object.values(state.detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === worktreeId)
)
resolveIndexedWorktreeOwner(state.worktreesByRepo, worktreeId).kind !== 'missing' ||
hasIndexedDetectedWorktree(state.detectedWorktreesByRepo, worktreeId)
const repoId = getRepoIdFromWorktreeId(worktreeId)
const hasKnownRepo = state.repos?.some((repo) => repo.id === repoId) === true
if (!hasKnownWorktree && !hasKnownRepo) {
@ -216,18 +217,14 @@ export function resolveExplicitWorktreeOperationRouteResult(
addRoute(exactRoutes, resolution.route)
}
}
for (const result of Object.values(state.detectedWorktreesByRepo ?? {})) {
for (const worktree of result.worktrees) {
if (worktree.id === worktreeId) {
exactRepoIds.add(worktree.repoId)
const resolution = resolveExactWorktreeRoute(state, worktree)
if (resolution.kind === 'ambiguous') {
return resolution
}
if (resolution.kind === 'resolved') {
addRoute(exactRoutes, resolution.route)
}
}
for (const worktree of findIndexedDetectedWorktrees(state.detectedWorktreesByRepo, worktreeId)) {
exactRepoIds.add(worktree.repoId)
const resolution = resolveExactWorktreeRoute(state, worktree)
if (resolution.kind === 'ambiguous') {
return resolution
}
if (resolution.kind === 'resolved') {
addRoute(exactRoutes, resolution.route)
}
}
if (exactRoutes.size > 0) {

View File

@ -0,0 +1,200 @@
import { writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import type { ExecutionHostId } from '../../../shared/execution-host'
import { hasIndexedDetectedWorktree } from './worktree-runtime-owner-index'
import { getExplicitRuntimeEnvironmentIdForWorktree } from './worktree-runtime-owner'
import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state'
const REPO_COUNT = 20
const WORKTREES_PER_REPO = 100
const LOOKUPS_PER_SWEEP = 200
const SWEEPS = 40
type OwnerRecord = {
id: string
repoId: string
hostId?: ExecutionHostId
runtimeOwnerEnvironmentId?: string
}
type DetectedByRepo = Record<string, { worktrees: readonly OwnerRecord[] }>
// The pre-index expression, kept as the "before" leg of the benchmark.
function walkHasDetected(detectedWorktreesByRepo: DetectedByRepo | undefined, id: string): boolean {
return Object.values(detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === id)
)
}
function buildDetectedCatalog(generation: number, counter?: { reads: number }): DetectedByRepo {
const catalog: DetectedByRepo = {}
for (let repoIndex = 0; repoIndex < REPO_COUNT; repoIndex += 1) {
const repoId = `repo-${repoIndex}`
const worktrees: OwnerRecord[] = []
for (let index = 0; index < WORKTREES_PER_REPO; index += 1) {
const id = `${repoId}::detected-${index}-gen-${generation}`
const record: OwnerRecord = { id, repoId, hostId: 'ssh:target-a' }
if (counter) {
Object.defineProperty(record, 'id', {
get: () => {
counter.reads += 1
return id
},
enumerable: true
})
}
worktrees.push(record)
}
catalog[repoId] = { worktrees }
}
return catalog
}
const publishedRepos = Array.from({ length: REPO_COUNT }, (_unused, index) => ({
id: `repo-${index}`,
connectionId: `target-${index}`
}))
const publishedWorktreesByRepo: Record<string, OwnerRecord[]> = Object.fromEntries(
publishedRepos.map((repo) => [
repo.id,
Array.from({ length: WORKTREES_PER_REPO }, (_unused, index) => ({
id: `${repo.id}::worktree-${index}`,
repoId: repo.id,
hostId: 'ssh:target-a' as const
}))
])
)
// Only the detected catalog changes identity: that is what isolates the collection under test.
function buildOwnerState(detectedWorktreesByRepo: DetectedByRepo): WorktreeRuntimeOwnerState {
return {
repos: publishedRepos,
worktreesByRepo: publishedWorktreesByRepo,
detectedWorktreesByRepo,
activeWorktreeId: null,
activeWorkspaceExecutionHostId: null,
runtimeEnvironments: []
}
}
// Probe ids are published but never detected — the SSH-pane case, and the walk's worst case.
const PROBE_IDS = Array.from(
{ length: LOOKUPS_PER_SWEEP },
(_unused, index) => `repo-${index % REPO_COUNT}::worktree-${index % WORKTREES_PER_REPO}`
)
const WARMUP_SWEEPS = 5
const warmCatalog = buildDetectedCatalog(-1)
// Each fresh leg needs its own never-indexed catalogs; a shared pool would leave the index warm
// for whichever leg runs second and silently erase the rebuild it is supposed to measure.
let catalogGeneration = 0
function freshCatalogPool(): DetectedByRepo[] {
return Array.from({ length: SWEEPS + WARMUP_SWEEPS }, () =>
buildDetectedCatalog((catalogGeneration += 1))
)
}
function measureSweeps(run: (sweep: number) => void): number {
for (let warmup = 0; warmup < WARMUP_SWEEPS; warmup += 1) {
run(warmup)
}
const started = performance.now()
for (let sweep = 0; sweep < SWEEPS; sweep += 1) {
run(WARMUP_SWEEPS + sweep)
}
return (performance.now() - started) / SWEEPS
}
describe('detected worktree index performance', () => {
it('answers repeated owner lookups without rescanning the detected catalog', () => {
const counter = { reads: 0 }
const catalog = buildDetectedCatalog(0, counter)
const state = buildOwnerState(catalog)
getExplicitRuntimeEnvironmentIdForWorktree(state, 'repo-0::warm-the-index')
counter.reads = 0
for (const probeId of PROBE_IDS) {
getExplicitRuntimeEnvironmentIdForWorktree(state, probeId)
}
const indexedReads = counter.reads
counter.reads = 0
for (const probeId of PROBE_IDS) {
walkHasDetected(catalog, probeId)
}
const walkedReads = counter.reads
expect(indexedReads).toBe(0)
expect(walkedReads).toBeGreaterThan(100_000)
})
it('records lookup-path timing without making wall clock a CI gate', () => {
const walkPool = freshCatalogPool()
const walkMs = measureSweeps((sweep) => {
const catalog = walkPool[sweep]!
for (const probeId of PROBE_IDS) {
walkHasDetected(catalog, probeId)
}
})
const walkWarmMs = measureSweeps(() => {
for (const probeId of PROBE_IDS) {
walkHasDetected(warmCatalog, probeId)
}
})
// Unrelated store writes leave the detected catalog identical, so the index stays warm.
const warmMs = measureSweeps(() => {
for (const probeId of PROBE_IDS) {
hasIndexedDetectedWorktree(warmCatalog, probeId)
}
})
// A landed worktree scan republishes the catalog: one rebuild amortized over the sweep.
const freshPool = freshCatalogPool()
const freshMs = measureSweeps((sweep) => {
const catalog = freshPool[sweep]!
for (const probeId of PROBE_IDS) {
hasIndexedDetectedWorktree(catalog, probeId)
}
})
const statePool = freshCatalogPool().map(buildOwnerState)
const warmState = buildOwnerState(warmCatalog)
const explicitOwnerFreshMs = measureSweeps((sweep) => {
const state = statePool[sweep]!
for (const probeId of PROBE_IDS) {
getExplicitRuntimeEnvironmentIdForWorktree(state, probeId)
}
})
const explicitOwnerWarmMs = measureSweeps(() => {
for (const probeId of PROBE_IDS) {
getExplicitRuntimeEnvironmentIdForWorktree(warmState, probeId)
}
})
const round = (value: number): number => Number(value.toFixed(4))
const report = {
entries: REPO_COUNT * WORKTREES_PER_REPO,
lookupsPerSweep: LOOKUPS_PER_SWEEP,
sweeps: SWEEPS,
lookupPath: {
walkFreshMsPerSweep: round(walkMs),
walkWarmMsPerSweep: round(walkWarmMs),
indexedWarmMsPerSweep: round(warmMs),
indexedFreshMsPerSweep: round(freshMs),
warmSpeedup: round(walkWarmMs / warmMs),
freshSpeedup: round(walkMs / freshMs)
},
getExplicitRuntimeEnvironmentIdForWorktree: {
warmMsPerSweep: round(explicitOwnerWarmMs),
freshMsPerSweep: round(explicitOwnerFreshMs)
}
}
writeFileSync(
join(tmpdir(), 'orca-detected-worktree-index-bench.json'),
`${JSON.stringify(report, null, 2)}\n`
)
// Why: shared-runner timing is noisy; the read-count test above is the deterministic CI gate.
})
})

View File

@ -0,0 +1,182 @@
import { describe, expect, it } from 'vitest'
import type { ExecutionHostId } from '../../../shared/execution-host'
import {
findIndexedDetectedWorktrees,
hasIndexedDetectedWorktree,
resolveIndexedWorktreeOwner
} from './worktree-runtime-owner-index'
type OwnerRecord = {
id: string
repoId: string
hostId?: ExecutionHostId
runtimeOwnerEnvironmentId?: string
}
type DetectedByRepo = Record<string, { worktrees: readonly OwnerRecord[] }>
// The pre-index expressions this module replaced, kept verbatim as parity oracles.
function walkHasDetected(detectedWorktreesByRepo: DetectedByRepo | undefined, id: string): boolean {
return Object.values(detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === id)
)
}
function walkDetectedMatches(
detectedWorktreesByRepo: DetectedByRepo | undefined,
id: string
): OwnerRecord[] {
const matches: OwnerRecord[] = []
for (const result of Object.values(detectedWorktreesByRepo ?? {})) {
for (const worktree of result.worktrees) {
if (worktree.id === id) {
matches.push(worktree)
}
}
}
return matches
}
function walkHasKnown(
worktreesByRepo: Record<string, readonly OwnerRecord[]> | undefined,
id: string
): boolean {
return Object.values(worktreesByRepo ?? {}).some((worktrees) =>
worktrees.some((worktree) => worktree.id === id)
)
}
// Deterministic LCG so a parity failure reproduces from the printed case index.
function makeRandom(seed: number): () => number {
let state = seed >>> 0
return () => {
state = (state * 1664525 + 1013904223) >>> 0
return state / 0x100000000
}
}
const HOST_IDS: (ExecutionHostId | undefined)[] = [
undefined,
'local',
'ssh:target-a',
'ssh:target-b',
'runtime:hub-a'
]
function buildCase(random: () => number): {
detectedWorktreesByRepo: DetectedByRepo | undefined
worktreesByRepo: Record<string, readonly OwnerRecord[]>
probeIds: string[]
} {
const shape = random()
if (shape < 0.05) {
return { detectedWorktreesByRepo: undefined, worktreesByRepo: {}, probeIds: ['repo-0::absent'] }
}
if (shape < 0.1) {
return { detectedWorktreesByRepo: {}, worktreesByRepo: {}, probeIds: ['repo-0::absent'] }
}
const repoCount = 1 + Math.floor(random() * 6)
const detectedWorktreesByRepo: DetectedByRepo = {}
const worktreesByRepo: Record<string, readonly OwnerRecord[]> = {}
const knownIds: string[] = []
for (let repoIndex = 0; repoIndex < repoCount; repoIndex += 1) {
const repoId = `repo-${repoIndex}`
// Empty arrays are a real store shape: a scan that found nothing still publishes its bucket.
const worktreeCount = random() < 0.25 ? 0 : 1 + Math.floor(random() * 5)
const detected: OwnerRecord[] = []
const published: OwnerRecord[] = []
for (let index = 0; index < worktreeCount; index += 1) {
// Duplicate ids across repos are how rival publications collide, so allow a shared pool.
const id = random() < 0.3 ? `shared::worktree-${index}` : `${repoId}::worktree-${index}`
const record: OwnerRecord = {
id,
repoId,
hostId: HOST_IDS[Math.floor(random() * HOST_IDS.length)],
...(random() < 0.3 ? { runtimeOwnerEnvironmentId: `hub-${Math.floor(random() * 3)}` } : {})
}
knownIds.push(id)
if (random() < 0.8) {
detected.push(record)
}
if (random() < 0.5) {
published.push(record)
}
}
detectedWorktreesByRepo[repoId] = { worktrees: detected }
worktreesByRepo[repoId] = published
}
const probeIds = [
knownIds[0] ?? 'repo-0::absent',
knownIds.at(-1) ?? 'repo-0::absent',
knownIds[Math.floor(random() * Math.max(knownIds.length, 1))] ?? 'repo-0::absent',
'shared::worktree-0',
'never-published::worktree'
]
return { detectedWorktreesByRepo, worktreesByRepo, probeIds }
}
describe('detected worktree index', () => {
it('matches the pre-index catalog walk across randomized store shapes', () => {
const random = makeRandom(0x5eed)
for (let caseIndex = 0; caseIndex < 240; caseIndex += 1) {
const { detectedWorktreesByRepo, worktreesByRepo, probeIds } = buildCase(random)
for (const probeId of probeIds) {
expect(
{
case: caseIndex,
probeId,
has: hasIndexedDetectedWorktree(detectedWorktreesByRepo, probeId)
},
`case ${caseIndex} / ${probeId}`
).toEqual({
case: caseIndex,
probeId,
has: walkHasDetected(detectedWorktreesByRepo, probeId)
})
expect(
findIndexedDetectedWorktrees(detectedWorktreesByRepo, probeId),
`case ${caseIndex} / ${probeId}`
).toEqual(walkDetectedMatches(detectedWorktreesByRepo, probeId))
expect(
resolveIndexedWorktreeOwner(worktreesByRepo, probeId).kind !== 'missing',
`case ${caseIndex} / ${probeId}`
).toBe(walkHasKnown(worktreesByRepo, probeId))
}
}
})
it('returns rival publications in catalog order with identity preserved', () => {
const first = { id: 'shared', repoId: 'repo-a', hostId: 'ssh:target-a' as const }
const second = { id: 'shared', repoId: 'repo-b', hostId: 'runtime:hub-a' as const }
const detectedWorktreesByRepo = {
'repo-a': { worktrees: [first] },
'repo-b': { worktrees: [second] }
}
const matches = findIndexedDetectedWorktrees(detectedWorktreesByRepo, 'shared')
expect(matches).toHaveLength(2)
expect(matches[0]).toBe(first)
expect(matches[1]).toBe(second)
})
it('treats undefined, empty, and empty-bucket catalogs as unpublished', () => {
expect(hasIndexedDetectedWorktree(undefined, 'repo::wt')).toBe(false)
expect(findIndexedDetectedWorktrees(undefined, 'repo::wt')).toEqual([])
expect(hasIndexedDetectedWorktree({}, 'repo::wt')).toBe(false)
expect(hasIndexedDetectedWorktree({ repo: { worktrees: [] } }, 'repo::wt')).toBe(false)
})
it('re-indexes a new catalog identity instead of serving stale hits', () => {
const worktree = { id: 'repo::wt', repoId: 'repo' }
const before = { repo: { worktrees: [worktree] } }
expect(hasIndexedDetectedWorktree(before, 'repo::wt')).toBe(true)
const afterRemoval = { repo: { worktrees: [] } }
expect(hasIndexedDetectedWorktree(afterRemoval, 'repo::wt')).toBe(false)
const afterReadd = { repo: { worktrees: [worktree] } }
expect(findIndexedDetectedWorktrees(afterReadd, 'repo::wt')).toEqual([worktree])
// The prior identity keeps its own cached answer; nothing bleeds between snapshots.
expect(hasIndexedDetectedWorktree(afterRemoval, 'repo::wt')).toBe(false)
expect(hasIndexedDetectedWorktree(before, 'repo::wt')).toBe(true)
})
})

View File

@ -8,6 +8,7 @@ import {
} from '../../../shared/execution-host'
type WorktreeOwnerRecord = Pick<Worktree, 'id' | 'repoId' | 'hostId' | 'runtimeOwnerEnvironmentId'>
type DetectedWorktreeListing = { worktrees: readonly WorktreeOwnerRecord[] }
type RepoOwnerRecord = Pick<Repo, 'id' | 'connectionId' | 'executionHostId'>
type FolderWorkspaceOwnerRecord = Pick<
FolderWorkspace,
@ -33,6 +34,12 @@ const projectGroupOwnerIndexCache = new WeakMap<
readonly ProjectGroupOwnerRecord[],
ReadonlyMap<string, IndexedProjectGroupOwnerResolution>
>()
const detectedWorktreeIndexCache = new WeakMap<
Record<string, DetectedWorktreeListing>,
ReadonlyMap<string, readonly WorktreeOwnerRecord[]>
>()
const NO_DETECTED_WORKTREES: readonly WorktreeOwnerRecord[] = []
type IndexedFolderWorkspaceOwnerResolution =
| { kind: 'resolved'; owner: FolderWorkspaceOwnerRecord }
@ -210,6 +217,43 @@ export function resolveIndexedWorktreeOwner(
return index.get(worktreeId) ?? { kind: 'missing' }
}
/**
* Every detected publication of `worktreeId`, in catalog order. Rival repos may publish the same
* id, so callers that fail closed on conflicts need all matches rather than one resolved owner.
*/
export function findIndexedDetectedWorktrees(
detectedWorktreesByRepo: Record<string, DetectedWorktreeListing> | undefined,
worktreeId: string
): readonly WorktreeOwnerRecord[] {
if (!detectedWorktreesByRepo) {
return NO_DETECTED_WORKTREES
}
let index = detectedWorktreeIndexCache.get(detectedWorktreesByRepo)
if (!index) {
const next = new Map<string, WorktreeOwnerRecord[]>()
for (const listing of Object.values(detectedWorktreesByRepo)) {
for (const worktree of listing.worktrees) {
const matches = next.get(worktree.id)
if (matches) {
matches.push(worktree)
} else {
next.set(worktree.id, [worktree])
}
}
}
index = next
detectedWorktreeIndexCache.set(detectedWorktreesByRepo, index)
}
return index.get(worktreeId) ?? NO_DETECTED_WORKTREES
}
export function hasIndexedDetectedWorktree(
detectedWorktreesByRepo: Record<string, DetectedWorktreeListing> | undefined,
worktreeId: string
): boolean {
return findIndexedDetectedWorktrees(detectedWorktreesByRepo, worktreeId).length > 0
}
export function findIndexedRepoOwner(
repos: readonly RepoOwnerRecord[] | undefined,
repoId: string

View File

@ -7,6 +7,7 @@ import { getRepoIdFromWorktreeId } from '@/store/slices/worktree-helpers'
import {
findIndexedRepoOwner as findRepoRecord,
findIndexedWorktreeOwner as findWorktreeRecord,
hasIndexedDetectedWorktree,
resolveIndexedRepoOwner,
resolveIndexedWorktreeOwner
} from './worktree-runtime-owner-index'
@ -79,9 +80,7 @@ export function getRuntimeEnvironmentIdForWorktree(
const owner = indexedOwner.owner
const projectedRuntimeOwner = getProjectedRuntimeOwnerEnvironmentId(owner)
const parsedHost = parseExecutionHostId(owner.hostId)
const hasDetectedOwner = Object.values(state.detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === worktreeId)
)
const hasDetectedOwner = hasIndexedDetectedWorktree(state.detectedWorktreesByRepo, worktreeId)
if (!hasDetectedOwner && (projectedRuntimeOwner || parsedHost)) {
return (
projectedRuntimeOwner || (parsedHost?.kind === 'runtime' ? parsedHost.environmentId : null)
@ -125,9 +124,7 @@ export function getExplicitRuntimeEnvironmentIdForWorktree(
workspaceScope.folderWorkspaceId
)
}
const hasDetectedOwner = Object.values(state.detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === worktreeId)
)
const hasDetectedOwner = hasIndexedDetectedWorktree(state.detectedWorktreesByRepo, worktreeId)
if (hasDetectedOwner) {
// Why: detected-only rows are selectable before the primary catalog lands; use the same
// ambiguity-aware explicit provenance as filesystem and terminal operations.
@ -177,9 +174,7 @@ export function getExecutionHostIdForWorktree(
if (workspaceScope?.type === 'folder') {
return getExecutionHostIdForFolderWorkspace(state, workspaceScope.folderWorkspaceId)
}
const hasDetectedOwner = Object.values(state.detectedWorktreesByRepo ?? {}).some((result) =>
result.worktrees.some((worktree) => worktree.id === worktreeId)
)
const hasDetectedOwner = hasIndexedDetectedWorktree(state.detectedWorktreesByRepo, worktreeId)
if (hasDetectedOwner) {
const resolution = resolveExplicitWorktreeOperationRouteResult(state, worktreeId)
if (resolution.kind === 'resolved') {