mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
perf(renderer): memoize the sleeping-record park exemption on slice identity
A pane title update writes runtimePaneTitlesByTabId, but zustand re-runs every mounted subscriber's selector, so each retained worktree walked the whole sleeping-agent inventory to conclude nothing changed for it. useShallow suppressed the re-render, never the scan. selectSleepingRecordParkExemptTabIds now goes through the existing createWorktreeRecordSelector generation cache, keyed on the record-map identity, so the walk happens once per worktree per real inventory change instead of once per store write. The cache moves from components/sidebar to store/ now that terminal-pane shares it, and takes an isEmpty override so a Set-valued selector can use it. 19,711 sleeping-record reads -> 0 for one title update at capture scale. Refs STA-7552, STA-7551
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
recordLiveEntriesFullRebuild
|
||||
} from './worktree-agent-live-index-patch'
|
||||
import { selectWorktreeAgentOrchestration } from './worktree-agent-orchestration-index'
|
||||
import { createWorktreeRecordSelector } from './worktree-record-selector-cache'
|
||||
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
|
||||
import type { TerminalLayoutSnapshot } from '../../../../shared/terminal-tab-types'
|
||||
|
||||
// Why frozen and exported: card hooks return these from their inactive branch,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AppState } from '@/store/types'
|
||||
import type { TerminalPaneLayoutNode } from '../../../../shared/terminal-tab-types'
|
||||
import { createWorktreeRecordSelector } from './worktree-record-selector-cache'
|
||||
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
|
||||
|
||||
// Why: these selectors return fresh maps whose top-level values preserve
|
||||
// underlying per-tab references, so callers must compare them shallowly.
|
||||
|
||||
+36
-34
@@ -33,7 +33,8 @@ import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
|
||||
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
|
||||
import type { TerminalTab } from '../../../../shared/terminal-tab-types'
|
||||
import { useTerminalTabColdParking } from './use-terminal-tab-cold-parking'
|
||||
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
|
||||
|
||||
Reflect.set(globalThis, 'IS_REACT_ACT_ENVIRONMENT', true)
|
||||
|
||||
const WORKSPACE_COUNT = 870
|
||||
const TERMINAL_TAB_COUNT = 1408
|
||||
@@ -82,15 +83,16 @@ function buildTabsByWorktree(): Record<string, TerminalTab[]> {
|
||||
for (const [index, worktreeId] of worktreeIds.entries()) {
|
||||
const count = Math.min(remaining, index < MOUNTED_WORKTREE_COUNT ? 4 : 2)
|
||||
remaining -= count
|
||||
tabsByWorktree[worktreeId] = Array.from(
|
||||
{ length: count },
|
||||
(_, tabIndex) =>
|
||||
({
|
||||
id: `tab-${index}-${tabIndex}`,
|
||||
title: `tab ${tabIndex}`,
|
||||
ptyId: `${worktreeId}@@pty-${tabIndex}`
|
||||
}) as TerminalTab
|
||||
)
|
||||
tabsByWorktree[worktreeId] = Array.from({ length: count }, (_, tabIndex) => ({
|
||||
id: `tab-${index}-${tabIndex}`,
|
||||
ptyId: `${worktreeId}@@pty-${tabIndex}`,
|
||||
worktreeId,
|
||||
title: `tab ${tabIndex}`,
|
||||
customTitle: null,
|
||||
color: null,
|
||||
sortOrder: tabIndex,
|
||||
createdAt: 0
|
||||
}))
|
||||
if (remaining <= 0) {
|
||||
break
|
||||
}
|
||||
@@ -107,15 +109,18 @@ function buildSleepingRecords(): Record<string, SleepingAgentSessionRecord> {
|
||||
const worktreeId = worktreeIds[index % WORKSPACE_COUNT]
|
||||
const tabId = tabsByWorktree[worktreeId]?.[0]?.id ?? `tab-${index}-0`
|
||||
const paneKey = `${tabId}:1`
|
||||
return [
|
||||
const record: SleepingAgentSessionRecord = {
|
||||
paneKey,
|
||||
{
|
||||
paneKey,
|
||||
tabId,
|
||||
worktreeId,
|
||||
providerSession: { id: `session-${index}` }
|
||||
} as unknown as SleepingAgentSessionRecord
|
||||
] as const
|
||||
tabId,
|
||||
worktreeId,
|
||||
agent: 'claude',
|
||||
providerSession: { key: 'session_id', id: `session-${index}` },
|
||||
prompt: 'prompt',
|
||||
state: 'working',
|
||||
capturedAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
return [paneKey, record] as const
|
||||
}),
|
||||
'sleepingRecords'
|
||||
)
|
||||
@@ -127,17 +132,16 @@ function buildAgentStatuses(): Record<string, AgentStatusEntry> {
|
||||
Array.from({ length: AGENT_STATUS_COUNT }, (_, index) => {
|
||||
const tabId = seededTerminalTabs[index % seededTerminalTabs.length].id
|
||||
const paneKey = `${tabId}:1`
|
||||
return [
|
||||
const entry: AgentStatusEntry = {
|
||||
paneKey,
|
||||
{
|
||||
paneKey,
|
||||
state: 'working',
|
||||
updatedAt: now,
|
||||
stateStartedAt: now,
|
||||
stateHistory: [],
|
||||
agentType: 'claude'
|
||||
} as unknown as AgentStatusEntry
|
||||
] as const
|
||||
state: 'working',
|
||||
prompt: 'prompt',
|
||||
updatedAt: now,
|
||||
stateStartedAt: now,
|
||||
stateHistory: [],
|
||||
agentType: 'claude'
|
||||
}
|
||||
return [paneKey, entry] as const
|
||||
}),
|
||||
'agentStatusRows'
|
||||
)
|
||||
@@ -175,7 +179,7 @@ function mountAtCaptureScale(): void {
|
||||
agentStatusEpoch: 1,
|
||||
activeWorktreeId: TARGET_WORKTREE_ID,
|
||||
runtimePaneTitlesByTabId: { [TARGET_TAB_ID]: { [TARGET_PANE_ID]: 'initial title' } }
|
||||
} as never)
|
||||
})
|
||||
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
@@ -212,10 +216,8 @@ afterEach(() => {
|
||||
useAppStore.setState(originalState, true)
|
||||
})
|
||||
|
||||
// Why `it.fails` in this commit: it pins the before-state in history. The fix
|
||||
// commit that follows flips both back to `it`.
|
||||
describe('one pane title update at live-capture scale', () => {
|
||||
it.fails('walks no global inventory', () => {
|
||||
it('walks no global inventory', () => {
|
||||
mountAtCaptureScale()
|
||||
reads.sleepingRecords = 0
|
||||
reads.agentStatusRows = 0
|
||||
@@ -227,7 +229,7 @@ describe('one pane title update at live-capture scale', () => {
|
||||
expect(MAIN_SLEEPING_RECORD_READS).toBe(SLEEPING_RECORD_COUNT * 23)
|
||||
})
|
||||
|
||||
it.fails('stays flat as unrelated workspaces accumulate', () => {
|
||||
it('stays flat as unrelated workspaces accumulate', () => {
|
||||
mountAtCaptureScale()
|
||||
reads.sleepingRecords = 0
|
||||
applyOnePaneTitleUpdate('title a')
|
||||
@@ -246,7 +248,7 @@ describe('one pane title update at live-capture scale', () => {
|
||||
reads.sleepingRecords = 0
|
||||
|
||||
act(() => {
|
||||
useAppStore.setState({ sleepingAgentSessionsByPaneKey: buildSleepingRecords() } as never)
|
||||
useAppStore.setState({ sleepingAgentSessionsByPaneKey: buildSleepingRecords() })
|
||||
})
|
||||
|
||||
// Why: correctness floor — a real inventory change must still be observed.
|
||||
|
||||
@@ -19,6 +19,12 @@ function sleepingRecord(
|
||||
}
|
||||
}
|
||||
|
||||
function stateWith(sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord>): {
|
||||
sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord>
|
||||
} {
|
||||
return { sleepingAgentSessionsByPaneKey }
|
||||
}
|
||||
|
||||
describe('selectSleepingRecordParkExemptTabIds', () => {
|
||||
it.each([
|
||||
[`tab-1:${LEAF_ID}`, 'tab-1'],
|
||||
@@ -26,14 +32,16 @@ describe('selectSleepingRecordParkExemptTabIds', () => {
|
||||
])('derives the owner from a valid pane key (%s)', (paneKey, tabId) => {
|
||||
const records = { [paneKey]: sleepingRecord({ paneKey }) }
|
||||
|
||||
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([tabId])
|
||||
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([tabId])
|
||||
})
|
||||
|
||||
it('prefers the persisted tab id over the pane key owner', () => {
|
||||
const paneKey = `tab-stale:${LEAF_ID}`
|
||||
const records = { [paneKey]: sleepingRecord({ paneKey, tabId: 'tab-current' }) }
|
||||
|
||||
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual(['tab-current'])
|
||||
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([
|
||||
'tab-current'
|
||||
])
|
||||
})
|
||||
|
||||
it('does not invent an owner for a delimiter-less pane key', () => {
|
||||
@@ -41,6 +49,30 @@ describe('selectSleepingRecordParkExemptTabIds', () => {
|
||||
'orphan-pane-key': sleepingRecord({ paneKey: 'orphan-pane-key' })
|
||||
}
|
||||
|
||||
expect([...selectSleepingRecordParkExemptTabIds(records, 'wt-1')]).toEqual([])
|
||||
expect([...selectSleepingRecordParkExemptTabIds(stateWith(records), 'wt-1')]).toEqual([])
|
||||
})
|
||||
|
||||
it('rebuilds when the record map changes and reuses the result when it does not', () => {
|
||||
const paneKey = `tab-1:${LEAF_ID}`
|
||||
const records = { [paneKey]: sleepingRecord({ paneKey }) }
|
||||
const state = stateWith(records)
|
||||
|
||||
const first = selectSleepingRecordParkExemptTabIds(state, 'wt-1')
|
||||
expect(selectSleepingRecordParkExemptTabIds(state, 'wt-1')).toBe(first)
|
||||
|
||||
const nextPaneKey = `tab-2:${LEAF_ID}`
|
||||
const grown = stateWith({ ...records, [nextPaneKey]: sleepingRecord({ paneKey: nextPaneKey }) })
|
||||
|
||||
expect([...selectSleepingRecordParkExemptTabIds(grown, 'wt-1')]).toEqual(['tab-1', 'tab-2'])
|
||||
})
|
||||
|
||||
// Why: a memo that serves a stale generation after the workspace's records are
|
||||
// dropped would pin a hidden pane mounted for the rest of the session.
|
||||
it('drops a worktree exemption once its records leave the map', () => {
|
||||
const paneKey = `tab-1:${LEAF_ID}`
|
||||
const populated = stateWith({ [paneKey]: sleepingRecord({ paneKey }) })
|
||||
expect([...selectSleepingRecordParkExemptTabIds(populated, 'wt-1')]).toEqual(['tab-1'])
|
||||
|
||||
expect([...selectSleepingRecordParkExemptTabIds(stateWith({}), 'wt-1')]).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,41 +1,58 @@
|
||||
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
|
||||
import { parseLegacyNumericPaneKey, parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { isPassiveCompletedHibernationEvidence } from '../../lib/sleeping-agent-pane-ownership'
|
||||
import { createWorktreeRecordSelector } from '@/store/worktree-record-selector-cache'
|
||||
|
||||
const EMPTY_TAB_IDS: ReadonlySet<string> = new Set()
|
||||
|
||||
export type SleepingRecordParkExemptionState = {
|
||||
sleepingAgentSessionsByPaneKey?: Record<string, SleepingAgentSessionRecord> | undefined
|
||||
}
|
||||
|
||||
/** Tab ids whose panes own a sleeping record a mount can actually consume.
|
||||
* Why: a parked pane can never cold-restore, so per-tab parks must exempt
|
||||
* these — but only these: passive-completed records never resume,
|
||||
* and exempting them would pin a hidden pane mounted indefinitely.
|
||||
* Callers subscribe through `useShallow`, which compares the set structurally,
|
||||
* so a write for another worktree cannot re-render this one. Iterates in place —
|
||||
* `Object.values` would allocate every record on every store write. */
|
||||
export function selectSleepingRecordParkExemptTabIds(
|
||||
sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord> | undefined,
|
||||
worktreeId: string
|
||||
): ReadonlySet<string> {
|
||||
if (!sleepingAgentSessionsByPaneKey) {
|
||||
return EMPTY_TAB_IDS
|
||||
*
|
||||
* Why memoized on the record-map identity (STA-7552): zustand re-runs every
|
||||
* mounted subscriber's selector on every store write, so an unrelated pane
|
||||
* title update used to walk the whole sleeping-record inventory once per
|
||||
* retained worktree. The map only changes when a record is parked or
|
||||
* consumed, so gating on its identity keeps a keystroke independent of how
|
||||
* many sleeping agents the profile has accumulated. Callers still compare with
|
||||
* `useShallow`; the carried generation makes that an identity hit.
|
||||
* Iterates in place — `Object.values` would allocate every record per rebuild. */
|
||||
export const selectSleepingRecordParkExemptTabIds = createWorktreeRecordSelector<
|
||||
SleepingRecordParkExemptionState,
|
||||
ReadonlySet<string>
|
||||
>({
|
||||
readSources: (state) => [state.sleepingAgentSessionsByPaneKey],
|
||||
empty: EMPTY_TAB_IDS,
|
||||
isEmpty: (tabIds) => tabIds.size === 0,
|
||||
build: (state, worktreeId) => {
|
||||
const sleepingAgentSessionsByPaneKey = state.sleepingAgentSessionsByPaneKey
|
||||
if (!sleepingAgentSessionsByPaneKey) {
|
||||
return EMPTY_TAB_IDS
|
||||
}
|
||||
let owned: Set<string> | null = null
|
||||
for (const paneKey in sleepingAgentSessionsByPaneKey) {
|
||||
const record = sleepingAgentSessionsByPaneKey[paneKey]
|
||||
if (!record || record.worktreeId !== worktreeId) {
|
||||
continue
|
||||
}
|
||||
if (isPassiveCompletedHibernationEvidence(record)) {
|
||||
continue
|
||||
}
|
||||
// Why: malformed pane keys must yield no owner instead of a truncated tab id.
|
||||
const tabId =
|
||||
record.tabId ??
|
||||
parsePaneKey(record.paneKey)?.tabId ??
|
||||
parseLegacyNumericPaneKey(record.paneKey)?.tabId
|
||||
if (tabId) {
|
||||
owned ??= new Set()
|
||||
owned.add(tabId)
|
||||
}
|
||||
}
|
||||
return owned ?? EMPTY_TAB_IDS
|
||||
}
|
||||
let owned: Set<string> | null = null
|
||||
for (const paneKey in sleepingAgentSessionsByPaneKey) {
|
||||
const record = sleepingAgentSessionsByPaneKey[paneKey]
|
||||
if (!record || record.worktreeId !== worktreeId) {
|
||||
continue
|
||||
}
|
||||
if (isPassiveCompletedHibernationEvidence(record)) {
|
||||
continue
|
||||
}
|
||||
// Why: malformed pane keys must yield no owner instead of a truncated tab id.
|
||||
const tabId =
|
||||
record.tabId ??
|
||||
parsePaneKey(record.paneKey)?.tabId ??
|
||||
parseLegacyNumericPaneKey(record.paneKey)?.tabId
|
||||
if (tabId) {
|
||||
owned ??= new Set()
|
||||
owned.add(tabId)
|
||||
}
|
||||
}
|
||||
return owned ?? EMPTY_TAB_IDS
|
||||
}
|
||||
})
|
||||
|
||||
@@ -119,9 +119,7 @@ export function useTerminalTabColdParking(args: {
|
||||
// Why the worktree-scoped set, not the record map: the map is app-global, so
|
||||
// subscribing to it re-rendered this worktree on every other worktree's write.
|
||||
const sleepingRecordOwnedTabIds = useAppStore(
|
||||
useShallow((state) =>
|
||||
selectSleepingRecordParkExemptTabIds(state.sleepingAgentSessionsByPaneKey, worktreeId)
|
||||
)
|
||||
useShallow((state) => selectSleepingRecordParkExemptTabIds(state, worktreeId))
|
||||
)
|
||||
const terminalTabHiddenSinceRef = useRef(new Map<string, number>())
|
||||
// Why: view switches hide every tab at once, so the park clock cannot rank them.
|
||||
|
||||
+3
-1
@@ -34,6 +34,8 @@ export function createWorktreeRecordSelector<TState, TValue extends object>(opti
|
||||
readSources: (state: TState) => readonly unknown[]
|
||||
build: (state: TState, worktreeId: string) => TValue
|
||||
empty: TValue
|
||||
/** Override for values whose emptiness is not `Object.keys` — a Set, for instance. */
|
||||
isEmpty?: (value: TValue) => boolean
|
||||
}): (state: TState, worktreeId: string) => TValue {
|
||||
let generation: WorktreeRecordGeneration<TValue> | null = null
|
||||
return (state, worktreeId) => {
|
||||
@@ -52,7 +54,7 @@ export function createWorktreeRecordSelector<TState, TValue extends object>(opti
|
||||
const built = options.build(state, worktreeId)
|
||||
const carried = generation.carried?.get(worktreeId)
|
||||
let value = built
|
||||
if (Object.keys(built).length === 0) {
|
||||
if (options.isEmpty ? options.isEmpty(built) : Object.keys(built).length === 0) {
|
||||
value = options.empty
|
||||
} else if (carried && shallow(carried, built)) {
|
||||
value = carried
|
||||
Reference in New Issue
Block a user