perf(bg): avoid full scans for exact session IDs (#2166)

* perf(bg): avoid full registry scans for exact session IDs

* fix(bg): keep resolver test seam internal
This commit is contained in:
Bogdan
2026-09-22 11:27:02 +08:00
committed by GitHub
parent 70fe2de2f8
commit 55e9b24d91
2 changed files with 280 additions and 4 deletions
+262 -2
View File
@@ -1,10 +1,13 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
import { createHash } from 'node:crypto'
import {
chmod,
mkdir,
mkdtemp,
readFile,
readdir,
rm,
stat,
symlink,
writeFile,
} from 'node:fs/promises'
@@ -44,6 +47,17 @@ const {
recordBackgroundSessionNaturalTerminationSync,
} = backgroundSessionRegistry
type Assert<T extends true> = T
type IsEqual<A, B> = (<T>() => T extends A ? 1 : 2) extends <
T,
>() => T extends B ? 1 : 2
? true
: false
type _ResolveBackgroundSessionParametersAreTargetOnly = Assert<
IsEqual<Parameters<typeof resolveBackgroundSession>, [target: string]>
>
describe('background session registry', () => {
let configDir: string
@@ -95,6 +109,47 @@ describe('background session registry', () => {
)
}
async function snapshotRegistryTree(
root: string,
relative = '',
): Promise<string[]> {
const current = join(root, relative)
const entries = await readdir(current, { withFileTypes: true })
const snapshot: string[] = []
for (const entry of entries.toSorted((a, b) =>
a.name.localeCompare(b.name),
)) {
const entryRelative = join(relative, entry.name)
if (entry.isDirectory()) {
snapshot.push(`${entryRelative}/`)
snapshot.push(...(await snapshotRegistryTree(root, entryRelative)))
continue
}
const path = join(root, entryRelative)
const metadata = await stat(path, { bigint: true })
snapshot.push(
`${entryRelative}:${metadata.mtimeNs}:${await readFile(path, 'utf8')}`,
)
}
return snapshot
}
async function resolveWithListCallCount(target: string): Promise<{
session: BackgroundSession
listCalls: number
}> {
let listCalls = 0
const session =
await backgroundSessionRegistry.__test.resolveBackgroundSessionWithList(
target,
async () => {
listCalls += 1
return await listBackgroundSessions()
},
)
return { session, listCalls }
}
beforeEach(async () => {
configDir = await mkdtemp(join(tmpdir(), 'openclaude-bg-registry-'))
_setBackgroundSessionsRootForTesting(join(configDir, 'bg-sessions'))
@@ -105,6 +160,10 @@ describe('background session registry', () => {
await rm(configDir, { force: true, recursive: true })
})
it('keeps the exported resolver signature free of test dependencies', () => {
expect(resolveBackgroundSession.length).toBe(1)
})
it('creates session metadata and log files under the OpenClaude config dir', async () => {
const session = await createBackgroundSession({
id: 'bg-test-1',
@@ -276,7 +335,9 @@ describe('background session registry', () => {
sessionId: 'conversation-2',
})
expect((await resolveBackgroundSession('bg-abc')).id).toBe('bg-named')
const { session, listCalls } = await resolveWithListCallCount('bg-abc')
expect(session.id).toBe('bg-named')
expect(listCalls).toBe(1)
})
it('resolves exact session ids before exact session names', async () => {
@@ -296,7 +357,206 @@ describe('background session registry', () => {
sessionId: 'conversation-name',
})
expect((await resolveBackgroundSession('bg-target')).id).toBe('bg-target')
const { session, listCalls } = await resolveWithListCallCount('bg-target')
expect(session.id).toBe('bg-target')
expect(listCalls).toBe(0)
})
it('resolves an exact session id without listing the metadata directory', async () => {
await createBackgroundSession({
id: 'bg-direct',
pid: 111,
cwd: '/repo',
command: ['openclaude', '--print', 'direct'],
sessionId: 'conversation-direct',
})
const metadataDir = join(configDir, 'bg-sessions', 'sessions')
await writeFile(join(metadataDir, 'unrelated-malformed.json'), '{')
const { session, listCalls } = await resolveWithListCallCount('bg-direct')
expect(session.id).toBe('bg-direct')
expect(listCalls).toBe(0)
})
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
'resolves an exact session id when directory enumeration is unavailable',
async () => {
await createBackgroundSession({
id: 'bg-direct-unlisted',
pid: 112,
cwd: '/repo',
command: ['openclaude', '--print', 'direct unlisted'],
sessionId: 'conversation-direct-unlisted',
})
const metadataDir = join(configDir, 'bg-sessions', 'sessions')
await chmod(metadataDir, 0o100)
try {
expect(
(await resolveBackgroundSession('bg-direct-unlisted')).id,
).toBe('bg-direct-unlisted')
} finally {
await chmod(metadataDir, 0o700)
}
},
)
it('applies natural terminal facts on the direct exact-id path', async () => {
await createBackgroundSession({
id: 'bg-direct-natural',
pid: 333,
cwd: '/repo',
command: ['openclaude', '--print', 'direct natural'],
sessionId: 'conversation-direct-natural',
})
await writeTerminalFact('bg-direct-natural', 'natural', {
pid: 333,
status: 'failed',
finishedAt: '2026-06-15T08:04:00.000Z',
exitCode: 23,
terminalReason: 'exit_code',
})
const { session, listCalls } = await resolveWithListCallCount(
'bg-direct-natural',
)
expect(session).toMatchObject({
status: 'failed',
finishedAt: '2026-06-15T08:04:00.000Z',
exitCode: 23,
terminalReason: 'exit_code',
})
expect(listCalls).toBe(0)
})
it('keeps killed facts strongest on the direct exact-id path', async () => {
await createBackgroundSession({
id: 'bg-direct-killed',
pid: 334,
cwd: '/repo',
command: ['openclaude', '--print', 'direct killed'],
sessionId: 'conversation-direct-killed',
})
await writeTerminalFact('bg-direct-killed', 'natural', {
pid: 334,
status: 'failed',
finishedAt: '2026-06-15T08:04:00.000Z',
exitCode: 17,
terminalReason: 'exit_code',
})
await writeTerminalFact('bg-direct-killed', 'killed', {
pid: 334,
status: 'killed',
finishedAt: '2026-06-15T08:05:00.000Z',
terminalReason: 'explicit_kill',
})
const { session, listCalls } = await resolveWithListCallCount(
'bg-direct-killed',
)
expect(session).toMatchObject({
status: 'killed',
finishedAt: '2026-06-15T08:04:00.000Z',
exitCode: 17,
terminalReason: 'explicit_kill',
})
expect(listCalls).toBe(0)
})
it('falls back to an exact live name when exact-id metadata is malformed', async () => {
await createBackgroundSession({
id: 'bg-name-owner',
name: 'bg-malformed',
pid: 335,
cwd: '/repo',
command: ['openclaude', '--print', 'name owner'],
sessionId: 'conversation-name-owner',
})
await writeFile(
join(configDir, 'bg-sessions', 'sessions', 'bg-malformed.json'),
'{',
)
const { session, listCalls } = await resolveWithListCallCount(
'bg-malformed',
)
expect(session.id).toBe('bg-name-owner')
expect(listCalls).toBe(1)
})
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
'falls back to an exact live name when exact-id metadata is unreadable',
async () => {
await createBackgroundSession({
id: 'bg-unreadable',
pid: 336,
cwd: '/repo',
command: ['openclaude', '--print', 'unreadable id'],
sessionId: 'conversation-unreadable-id',
})
await createBackgroundSession({
id: 'bg-unreadable-name-owner',
name: 'bg-unreadable',
pid: 337,
cwd: '/repo',
command: ['openclaude', '--print', 'unreadable name owner'],
sessionId: 'conversation-unreadable-name-owner',
})
const exactPath = join(
configDir,
'bg-sessions',
'sessions',
'bg-unreadable.json',
)
await chmod(exactPath, 0o000)
try {
const { session, listCalls } = await resolveWithListCallCount(
'bg-unreadable',
)
expect(session.id).toBe('bg-unreadable-name-owner')
expect(listCalls).toBe(1)
} finally {
await chmod(exactPath, 0o600)
}
},
)
it('does not interpret unsafe exact-id candidates as metadata paths', async () => {
const named = await createBackgroundSession({
id: 'bg-safe-name-owner',
name: '../outside',
pid: 338,
cwd: '/repo',
command: ['openclaude', '--print', 'safe name owner'],
sessionId: 'conversation-safe-name-owner',
})
await writeFile(
join(configDir, 'bg-sessions', 'outside.json'),
JSON.stringify({ ...named, id: 'outside' }),
)
expect((await resolveBackgroundSession('../outside')).id).toBe(
'bg-safe-name-owner',
)
})
it('does not write registry state while resolving an exact id', async () => {
await createBackgroundSession({
id: 'bg-read-only',
pid: 339,
cwd: '/repo',
command: ['openclaude', '--print', 'read only'],
sessionId: 'conversation-read-only',
})
const root = join(configDir, 'bg-sessions')
const before = await snapshotRegistryTree(root)
expect((await resolveBackgroundSession('bg-read-only')).id).toBe(
'bg-read-only',
)
expect(await snapshotRegistryTree(root)).toEqual(before)
})
it('resolves unique session id prefixes when no exact name matches', async () => {
+18 -2
View File
@@ -2538,10 +2538,16 @@ export async function createBackgroundSession(
return session
}
export async function resolveBackgroundSession(
async function resolveBackgroundSessionWithList(
target: string,
listSessions: () => Promise<BackgroundSession[]>,
): Promise<BackgroundSession> {
const sessions = await listBackgroundSessions()
if (SAFE_ID_RE.test(target)) {
const exact = await readSessionFile(metadataPathForId(target))
if (exact) return await applyAuthoritativeTerminalFacts(exact)
}
const sessions = await listSessions()
const exactId = sessions.filter(s => s.id === target)
if (exactId.length === 1) return exactId[0]
@@ -2566,6 +2572,16 @@ export async function resolveBackgroundSession(
throw new Error(`No background session found for "${target}"`)
}
export async function resolveBackgroundSession(
target: string,
): Promise<BackgroundSession> {
return await resolveBackgroundSessionWithList(target, listBackgroundSessions)
}
export const __test = {
resolveBackgroundSessionWithList,
}
export async function refreshBackgroundSessionStatuses(options?: {
isProcessAlive?: (pid: number) => boolean
getProcessCommand?: (pid: number) => string | null