Files
openchamber/packages/ui/src/sync/performance-diagnostics.ts
T
Bohdan Triapitsyn 85400459e9 perf: overhaul session loading, caching, and runtime isolation (#2360)
Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.

- prioritize selected and visible sessions during bootstrap and defer
  non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
  hidden sidebar work
- prevent stale session and message requests from overwriting newer
  authoritative state
- preserve existing data when authoritative fetches fail instead of treating
  failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
  permissions, folders, tabs, Git state, and pull request data by runtime and
  directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
  persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
  message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
  bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
  runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
  rendering hot paths
- limit virtualization to archive collections where it improves rendering
  without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
  sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
  flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
  reconnect behavior, persistence races, authoritative empty results, and
  subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
  runtime-isolation invariants
2026-07-21 20:52:20 +03:00

127 lines
3.9 KiB
TypeScript

const STORAGE_KEY = "openchamber_sync_perf"
declare global {
interface Window {
__openchamberSyncPerformance?: {
getSnapshot: () => SyncPerformanceCounters | null
reset: () => void
}
}
}
export type SyncPerformanceCounters = {
pipelineRawEvents: number
pipelineCoalescedEvents: number
pipelineDeliveredEvents: number
reducerEvents: number
reducerChangedEvents: number
directoryStorePublications: number
streamingFullReconciliations: number
streamingIncrementalReconciliations: number
streamingStatusEntriesVisited: number
streamingSessionCandidatesVisited: number
streamingMessagesVisited: number
streamingHeartbeatAttempts: number
streamingHeartbeatCommits: number
permissionChangeCallbacks: number
sessionMessageChangeCallbacks: number
sessionRenderableNotificationSkips: number
userMessageHistoryNotificationSkips: number
sessionMessageRecordNotificationSkips: number
materializationEnqueues: number
materializationEmptyAssistantEnqueues: number
materializationMissingMessageEnqueues: number
materializationMissingPartEnqueues: number
materializationLifecycleEnqueues: number
materializationPreflightSkips: number
materializationRequests: number
statusAggregationSessionEntries: number
statusAggregationCandidates: number
persistenceSerializations: number
persistenceStorageWrites: number
persistenceUtf8Bytes: number
}
const createCounters = (): SyncPerformanceCounters => ({
pipelineRawEvents: 0,
pipelineCoalescedEvents: 0,
pipelineDeliveredEvents: 0,
reducerEvents: 0,
reducerChangedEvents: 0,
directoryStorePublications: 0,
streamingFullReconciliations: 0,
streamingIncrementalReconciliations: 0,
streamingStatusEntriesVisited: 0,
streamingSessionCandidatesVisited: 0,
streamingMessagesVisited: 0,
streamingHeartbeatAttempts: 0,
streamingHeartbeatCommits: 0,
permissionChangeCallbacks: 0,
sessionMessageChangeCallbacks: 0,
sessionRenderableNotificationSkips: 0,
userMessageHistoryNotificationSkips: 0,
sessionMessageRecordNotificationSkips: 0,
materializationEnqueues: 0,
materializationEmptyAssistantEnqueues: 0,
materializationMissingMessageEnqueues: 0,
materializationMissingPartEnqueues: 0,
materializationLifecycleEnqueues: 0,
materializationPreflightSkips: 0,
materializationRequests: 0,
statusAggregationSessionEntries: 0,
statusAggregationCandidates: 0,
persistenceSerializations: 0,
persistenceStorageWrites: 0,
persistenceUtf8Bytes: 0,
})
const readInitialEnabled = (): boolean => {
if (typeof window === "undefined") return false
try {
return window.localStorage.getItem(STORAGE_KEY) === "1"
} catch {
return false
}
}
let activeCounters: SyncPerformanceCounters | null = readInitialEnabled() ? createCounters() : null
export function setSyncPerformanceDiagnosticsEnabled(enabled: boolean): void {
activeCounters = enabled ? createCounters() : null
}
export function resetSyncPerformanceDiagnostics(): void {
if (activeCounters) activeCounters = createCounters()
}
export function getSyncPerformanceDiagnostics(): SyncPerformanceCounters | null {
return activeCounters ? { ...activeCounters } : null
}
export function countSyncPerformance(
counter: keyof SyncPerformanceCounters,
amount = 1,
): void {
const counters = activeCounters
if (!counters) return
counters[counter] += amount
}
export function countSyncPersistenceSerialization(serialized: string): void {
const counters = activeCounters
if (!counters) return
counters.persistenceSerializations += 1
counters.persistenceUtf8Bytes += new TextEncoder().encode(serialized).byteLength
}
export function countSyncPersistenceStorageWrite(): void {
if (activeCounters) activeCounters.persistenceStorageWrites += 1
}
if (typeof window !== "undefined") {
window.__openchamberSyncPerformance = {
getSnapshot: getSyncPerformanceDiagnostics,
reset: resetSyncPerformanceDiagnostics,
}
}