perf: optimize session loading and desktop startup (#2545)
* perf: optimize session loading and startup * fix(chat): stabilize history prepend virtualization * perf: unblock first session open from startup network contention Opening the first session after app start waited seconds for its message fetch. Three independent contributors, each measured via CDP network capture and Chromium net-log against the packaged desktop app: - The active-session watchdog fired an uncapped per-directory status poll and child-session discovery burst at startup, and other subsystems (git checks, global session pages, command/skill discovery) fanned out alongside it, saturating the browser's ~6 HTTP/1.1 sockets per origin. Add a shared background-network gate (concurrency 3) and route the watchdog, poll-shaped git reads (also priority: low), global session pages, command/skill loads, and the background update check through it. - The packaged renderer is cross-origin to the loopback backend, so every API call needs a CORS preflight; a few slow OpenCode-proxied requests held the whole pool while preflights and interactive traffic queued behind them. Lift Chromium's per-host connection cap for loopback via ignore-connections-limit in the Electron shell. - OpenCode initializes each directory lazily on its first request, so the first click paid that cost interactively. Warm the last-used directory and the three most recently opened projects right after OpenCode readiness, sequentially and best-effort, overlapping UI startup. Validation: new background-network tests, lifecycle warmup test, focused store/sync tests, UI type-check and lint, dead-code report, node --check plus electron type-check/lint, and CDP first-open measurements on the packaged app (message fetch socket queue 5.4s -> 0.03s). * fix(ui): keep interactive git reads out of background queue --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
09f0c64839
commit
aae889b904
@@ -2,6 +2,10 @@ import { describe, expect, test } from "bun:test"
|
||||
import type { Message, OpencodeClient, Part } from "@opencode-ai/sdk/v2/client"
|
||||
import { ChildStoreManager } from "./child-store"
|
||||
import { SessionMessageLoader } from "./session-message-loader"
|
||||
import {
|
||||
createFirstVisibleSessionPerformanceTracker,
|
||||
startSessionLoadPerformanceEvent,
|
||||
} from "./session-load-performance"
|
||||
|
||||
const createRecord = (sessionID: string, id = "msg_1") => ({
|
||||
info: { id, sessionID, role: "user", time: { created: 1 } } as Message,
|
||||
@@ -56,6 +60,34 @@ describe("SessionMessageLoader", () => {
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("leaves older history loading to explicit viewport demand", async () => {
|
||||
const calls: Array<{ limit?: number; before?: string }> = []
|
||||
const { childStores, loader } = createLoader(async ({ sessionID, limit, before }) => {
|
||||
calls.push({ limit, before })
|
||||
return before
|
||||
? response([createRecord(sessionID, "msg_older")])
|
||||
: response([createRecord(sessionID, "msg_latest")], "older-cursor")
|
||||
})
|
||||
const target = { directory: "/repo", sessionID: "session-a" }
|
||||
|
||||
await loader.ensure(target, { reason: "prefetch" })
|
||||
await Promise.resolve()
|
||||
|
||||
expect(calls).toEqual([{ limit: 50, before: undefined }])
|
||||
expect(loader.getSnapshot(target).cursor).toBe("older-cursor")
|
||||
|
||||
await loader.loadOlder(target)
|
||||
|
||||
expect(calls).toEqual([
|
||||
{ limit: 50, before: undefined },
|
||||
{ limit: 100, before: "older-cursor" },
|
||||
])
|
||||
expect(childStores.getChild(target.directory)?.getState().message[target.sessionID]?.map((message) => message.id))
|
||||
.toEqual(["msg_latest", "msg_older"].sort())
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("runs a requested tail refresh after an older in-flight load", async () => {
|
||||
const initial = deferred<ReturnType<typeof response>>()
|
||||
const refresh = deferred<ReturnType<typeof response>>()
|
||||
@@ -128,6 +160,34 @@ describe("SessionMessageLoader", () => {
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("loads older history with the selected directory's cursor for duplicate session IDs", async () => {
|
||||
const providerDirectory = "/repo/provider"
|
||||
const selectedDirectory = "/repo/selected-worktree"
|
||||
const sessionID = "shared"
|
||||
const calls: Array<{ directory?: string; before?: string }> = []
|
||||
const { childStores, loader } = createLoader(async ({ directory, before }) => {
|
||||
calls.push({ directory, before })
|
||||
return before
|
||||
? response([createRecord(sessionID, `older-${directory}`)])
|
||||
: response([createRecord(sessionID, `latest-${directory}`)], `${directory}-cursor`)
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
loader.ensure({ directory: providerDirectory, sessionID }),
|
||||
loader.ensure({ directory: selectedDirectory, sessionID }),
|
||||
])
|
||||
calls.length = 0
|
||||
|
||||
await loader.loadOlder({ directory: selectedDirectory, sessionID })
|
||||
|
||||
expect(calls).toEqual([{
|
||||
directory: selectedDirectory,
|
||||
before: `${selectedDirectory}-cursor`,
|
||||
}])
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("exposes a retryable error without clearing an existing snapshot", async () => {
|
||||
let fail = true
|
||||
const { childStores, loader } = createLoader(async ({ sessionID }) => {
|
||||
@@ -209,4 +269,165 @@ describe("SessionMessageLoader", () => {
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
})
|
||||
|
||||
test("reports retries and every downloaded initial expansion record", async () => {
|
||||
const originalWindow = Object.getOwnPropertyDescriptor(globalThis, "window")
|
||||
const diagnosticWindow = {
|
||||
location: { search: "" },
|
||||
localStorage: {
|
||||
getItem: (key: string) => key === "openchamber_session_load_perf" ? "1" : null,
|
||||
},
|
||||
} as unknown as Window
|
||||
Object.defineProperty(globalThis, "window", { configurable: true, value: diagnosticWindow })
|
||||
|
||||
const target = { directory: "/repo", sessionID: "session-a" }
|
||||
let calls = 0
|
||||
const { childStores, loader } = createLoader(async () => {
|
||||
calls += 1
|
||||
if (calls === 1) return {}
|
||||
if (calls === 2) {
|
||||
const assistant = createRecord(target.sessionID, "msg_assistant")
|
||||
assistant.info = { ...assistant.info, role: "assistant" } as Message
|
||||
return response([assistant], "older")
|
||||
}
|
||||
return response([createRecord(target.sessionID, "msg_user")])
|
||||
})
|
||||
|
||||
try {
|
||||
await loader.ensure(target)
|
||||
|
||||
const events = diagnosticWindow.__openchamberSessionLoadPerformance?.events ?? []
|
||||
const initialEvent = events.find((event) => event.operation === "session-messages.initial")
|
||||
const pageEvents = events.filter((event) => event.operation === "session-messages.page")
|
||||
expect(calls).toBe(3)
|
||||
expect(pageEvents.map((event) => event.requestLimit)).toEqual([50, 100])
|
||||
expect(pageEvents.map((event) => event.cursorPresent)).toEqual([false, false])
|
||||
expect(pageEvents.map((event) => event.recordCount)).toEqual([1, 1])
|
||||
expect(initialEvent?.outcome).toBe("complete")
|
||||
expect(initialEvent?.retryCount).toBe(1)
|
||||
expect(initialEvent?.recordCount).toBe(2)
|
||||
expect("runtimeKey" in initialEvent!).toBe(false)
|
||||
expect("directory" in initialEvent!).toBe(false)
|
||||
expect("sessionID" in initialEvent!).toBe(false)
|
||||
} finally {
|
||||
loader.dispose()
|
||||
childStores.disposeAll()
|
||||
if (originalWindow) Object.defineProperty(globalThis, "window", originalWindow)
|
||||
else Reflect.deleteProperty(globalThis, "window")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("session load performance diagnostics", () => {
|
||||
test("rejects unknown raw labels and preserves approved input counts", () => {
|
||||
const originalWindow = Object.getOwnPropertyDescriptor(globalThis, "window")
|
||||
const diagnosticWindow = {
|
||||
localStorage: {
|
||||
getItem: (key: string) => key === "openchamber_session_load_perf" ? "1" : null,
|
||||
},
|
||||
} as unknown as Window
|
||||
Object.defineProperty(globalThis, "window", { configurable: true, value: diagnosticWindow })
|
||||
|
||||
try {
|
||||
const finishUnknown = startSessionLoadPerformanceEvent({
|
||||
operation: "secret-operation",
|
||||
caller: "secret-caller",
|
||||
recordCount: 999,
|
||||
})
|
||||
finishUnknown("complete")
|
||||
const finishVisible = startSessionLoadPerformanceEvent({
|
||||
operation: "session-messages.visible",
|
||||
caller: "selected-session",
|
||||
recordCount: 30,
|
||||
})
|
||||
finishVisible("complete")
|
||||
|
||||
expect(diagnosticWindow.__openchamberSessionLoadPerformance?.events).toHaveLength(1)
|
||||
const event = diagnosticWindow.__openchamberSessionLoadPerformance?.events[0]
|
||||
expect(event?.operation).toBe("session-messages.visible")
|
||||
expect(event?.caller).toBe("selected-session")
|
||||
expect(event?.recordCount).toBe(30)
|
||||
expect(JSON.stringify(diagnosticWindow.__openchamberSessionLoadPerformance)).not.toContain("secret")
|
||||
} finally {
|
||||
if (originalWindow) Object.defineProperty(globalThis, "window", originalWindow)
|
||||
else Reflect.deleteProperty(globalThis, "window")
|
||||
}
|
||||
})
|
||||
|
||||
test("does not schedule visibility work while diagnostics are disabled", () => {
|
||||
let requestedFrames = 0
|
||||
let visibleMarks = 0
|
||||
const tracker = createFirstVisibleSessionPerformanceTracker({
|
||||
enabled: () => false,
|
||||
requestFrame: () => {
|
||||
requestedFrames += 1
|
||||
return 1
|
||||
},
|
||||
cancelFrame: () => undefined,
|
||||
markVisible: () => {
|
||||
visibleMarks += 1
|
||||
},
|
||||
})
|
||||
|
||||
tracker.schedule("session-a", 10)
|
||||
|
||||
expect(requestedFrames).toBe(0)
|
||||
expect(visibleMarks).toBe(0)
|
||||
})
|
||||
|
||||
test("reschedules an identity when its pending visibility frame was canceled", () => {
|
||||
let nextFrame = 0
|
||||
const frames = new Map<number, FrameRequestCallback>()
|
||||
const marks: string[] = []
|
||||
const tracker = createFirstVisibleSessionPerformanceTracker({
|
||||
enabled: () => true,
|
||||
requestFrame: (callback) => {
|
||||
nextFrame += 1
|
||||
frames.set(nextFrame, callback)
|
||||
return nextFrame
|
||||
},
|
||||
cancelFrame: (frame) => {
|
||||
frames.delete(frame)
|
||||
},
|
||||
markVisible: () => marks.push("visible"),
|
||||
startEvent: () => () => undefined,
|
||||
})
|
||||
|
||||
const cancelFirstA = tracker.schedule("session-a", 10)
|
||||
cancelFirstA()
|
||||
const cancelB = tracker.schedule("session-b", 10)
|
||||
cancelB()
|
||||
tracker.schedule("session-a", 10)
|
||||
frames.get(3)?.(0)
|
||||
|
||||
expect(marks).toEqual(["visible"])
|
||||
})
|
||||
|
||||
test("does not remeasure a completed identity after another session", () => {
|
||||
let nextFrame = 0
|
||||
const frames = new Map<number, FrameRequestCallback>()
|
||||
const marks: string[] = []
|
||||
const tracker = createFirstVisibleSessionPerformanceTracker({
|
||||
enabled: () => true,
|
||||
requestFrame: (callback) => {
|
||||
nextFrame += 1
|
||||
frames.set(nextFrame, callback)
|
||||
return nextFrame
|
||||
},
|
||||
cancelFrame: (frame) => {
|
||||
frames.delete(frame)
|
||||
},
|
||||
markVisible: () => marks.push("visible"),
|
||||
startEvent: () => () => undefined,
|
||||
})
|
||||
|
||||
tracker.schedule("session-a", 10)
|
||||
frames.get(1)?.(0)
|
||||
tracker.schedule("session-b", 10)
|
||||
frames.get(2)?.(0)
|
||||
tracker.schedule("session-a", 10)
|
||||
|
||||
expect(nextFrame).toBe(2)
|
||||
expect(marks).toEqual(["visible", "visible"])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user