mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 10:37:35 +00:00
Reuse Explorer watcher child indexes across unchanged payloads (#35319)
* perf(explorer): reuse watcher child indexes by listing identity * test(explorer): trim child index reuse tests to reuse and invalidation --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { DirCache, TreeNode } from './file-explorer-types'
|
||||
import { processFileExplorerFsPayload } from './file-explorer-watch-reconcile'
|
||||
|
||||
function reconcile(
|
||||
cache: Record<string, DirCache>,
|
||||
absolutePath: string
|
||||
): { dir: ReturnType<typeof vi.fn>; tree: ReturnType<typeof vi.fn> } {
|
||||
const dir = vi.fn()
|
||||
const tree = vi.fn()
|
||||
processFileExplorerFsPayload({
|
||||
payload: {
|
||||
worktreePath: '/repo',
|
||||
events: [{ kind: 'update', absolutePath, isDirectory: false }]
|
||||
},
|
||||
currentWorktreePath: '/repo',
|
||||
worktreeId: 'wt-1',
|
||||
cache,
|
||||
expanded: new Set(),
|
||||
setDirCache: vi.fn(),
|
||||
setSelectedPath: vi.fn(),
|
||||
refreshDir: dir,
|
||||
refreshTree: tree
|
||||
})
|
||||
return { dir, tree }
|
||||
}
|
||||
|
||||
function node(path: string): TreeNode {
|
||||
return {
|
||||
path,
|
||||
name: path,
|
||||
relativePath: path.slice('/repo/'.length),
|
||||
isDirectory: false,
|
||||
isSymlink: false,
|
||||
depth: 0,
|
||||
operationOwner: { kind: 'local' }
|
||||
}
|
||||
}
|
||||
|
||||
describe('watcher sibling index reuse', () => {
|
||||
it('indexes unchanged siblings once across update payloads', () => {
|
||||
let reads = 0
|
||||
const children = Array.from({ length: 1_000 }, (_, index) => {
|
||||
const path = `/repo/file-${index}.ts`
|
||||
const child = node(path)
|
||||
Object.defineProperty(child, 'path', {
|
||||
get: () => {
|
||||
reads++
|
||||
return path
|
||||
}
|
||||
})
|
||||
return child
|
||||
})
|
||||
const cache = { '/repo': { children } }
|
||||
for (let payload = 0; payload < 10; payload++) {
|
||||
const refresh = reconcile(cache, '/repo/file-0.ts')
|
||||
expect(refresh.dir).not.toHaveBeenCalled()
|
||||
expect(refresh.tree).not.toHaveBeenCalled()
|
||||
}
|
||||
expect(reads).toBe(1_000)
|
||||
})
|
||||
|
||||
it('reindexes replaced listings while reusing an unchanged array through cache copies', () => {
|
||||
const children = [node('/repo/old.ts')]
|
||||
const before = { '/repo': { children } }
|
||||
expect(reconcile(before, '/repo/old.ts').dir).not.toHaveBeenCalled()
|
||||
expect(reconcile({ ...before }, '/repo/old.ts').dir).not.toHaveBeenCalled()
|
||||
|
||||
const after = { '/repo': { children: [node('/repo/new.ts')] } }
|
||||
expect(reconcile(after, '/repo/new.ts').dir).not.toHaveBeenCalled()
|
||||
expect(reconcile(after, '/repo/old.ts').dir).toHaveBeenCalledWith('/repo')
|
||||
})
|
||||
})
|
||||
@@ -31,21 +31,23 @@ export type ProcessFileExplorerFsPayloadArgs = {
|
||||
refreshTree: () => void
|
||||
}
|
||||
|
||||
const cachedChildPathIndexes = new WeakMap<readonly TreeNode[], ReadonlyMap<string, TreeNode>>()
|
||||
|
||||
function cachedDirectoryChild(
|
||||
cache: Record<string, DirCache>,
|
||||
cachedDirPath: string,
|
||||
childPath: string,
|
||||
childPathIndexes: Map<string, Map<string, TreeNode>>
|
||||
childPath: string
|
||||
): TreeNode | undefined {
|
||||
let childPaths = childPathIndexes.get(cachedDirPath)
|
||||
const children = cache[cachedDirPath]?.children
|
||||
if (!children) {
|
||||
return undefined
|
||||
}
|
||||
let childPaths = cachedChildPathIndexes.get(children)
|
||||
if (!childPaths) {
|
||||
childPaths = new Map(
|
||||
cache[cachedDirPath]?.children.map((child) => [
|
||||
normalizeRuntimePathForComparison(child.path),
|
||||
child
|
||||
]) ?? []
|
||||
children.map((child) => [normalizeRuntimePathForComparison(child.path), child])
|
||||
)
|
||||
childPathIndexes.set(cachedDirPath, childPaths)
|
||||
cachedChildPathIndexes.set(children, childPaths)
|
||||
}
|
||||
return childPaths.get(normalizeRuntimePathForComparison(childPath))
|
||||
}
|
||||
@@ -83,7 +85,6 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
|
||||
}
|
||||
|
||||
const dirsToRefresh = new Set<string>()
|
||||
const childPathIndexes = new Map<string, Map<string, TreeNode>>()
|
||||
let hasLinkedCache: boolean | undefined
|
||||
let cachedDirKeys: string[] | undefined
|
||||
let cachedDirPathIndex: ReadonlyMap<string, string> | undefined
|
||||
@@ -114,7 +115,7 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
|
||||
const cachedParent = resolveCachedDirPath(cache, parent, currentWorktreePath, cachePathIndex)
|
||||
const updatedChild =
|
||||
evt.kind === 'update' && cachedParent
|
||||
? cachedDirectoryChild(cache, cachedParent, normalizedPath, childPathIndexes)
|
||||
? cachedDirectoryChild(cache, cachedParent, normalizedPath)
|
||||
: undefined
|
||||
const knownFileUpdate =
|
||||
evt.kind === 'update' &&
|
||||
@@ -227,7 +228,7 @@ export function processFileExplorerFsPayload(args: ProcessFileExplorerFsPayloadA
|
||||
if (
|
||||
cachedParent &&
|
||||
!dirsToRefresh.has(cachedParent) &&
|
||||
!cachedDirectoryChild(cache, cachedParent, normalizedPath, childPathIndexes)
|
||||
!cachedDirectoryChild(cache, cachedParent, normalizedPath)
|
||||
) {
|
||||
dirsToRefresh.add(cachedParent)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user