mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 08:04:05 +00:00
* fix(agent-history): keep the app responsive while scanning huge OpenCode databases (#8864) Opening or refreshing Agent Session History froze the entire app when OpenCode's opencode.db had grown to multiple GB (reporter: 29 GB with 20+ live opencode writers). All OpenCode SQLite reads ran synchronously (node:sqlite DatabaseSync) on the Electron main-process event loop, the discovery query evaluated a COUNT+json_extract subquery for every session before LIMIT, and the preview query JSON-parsed every part blob of each session. Live writers bump session.time_updated continuously, so the mtime parse cache missed every 15s/focus/manual refresh and the multi-tens-of-seconds freeze repeated forever. Fix: - Run OpenCode SQLite discovery and per-session parsing on a persistent worker thread (lazy spawn, unref'd, idle teardown, FIFO dispatch, per-call timeouts, crash-loop cap). Faults surface as per-source scan issues instead of stalls; other providers' results always arrive. - Fall back to the in-process reader when no worker bundle exists, surfacing a degraded-mode scan issue so a persistent fallback cannot silently reintroduce the hang. - Bound the queries: sort+LIMIT sessions before computing message counts, and source previews from the newest 100 messages via the (session_id, time_created, id) index instead of scanning every part. Measured on a 22 GB synthetic DB matching the reporter's shape: main- process IPC RTT during a fully cache-invalidated scan went from a 32.8 s continuous block (UI click timeout) to 1 ms max, with the panel populating normally (previews and message counts intact). * fix(agent-history): drop the cached worker handle after a clean exit and document worker-client exports A worker that exits cleanly on its own left this.worker pointing at the dead thread; the next request would post into it and stall to its timeout instead of respawning. Clean idle exits now drop the handle without counting as a death. Also adds JSDoc to the new exported scan-worker surfaces. * fix(agent-history): keep OpenCode scans off the main thread * fix(agent-history): align OpenCode recency ordering
59 lines
2.1 KiB
TypeScript
59 lines
2.1 KiB
TypeScript
import { parentPort } from 'node:worker_threads'
|
|
import type { AiVaultScanIssue } from '../../shared/ai-vault-types'
|
|
import { listOpenCodeSqliteSessions } from './session-scanner-opencode-sqlite-list'
|
|
import { parseOpenCodeSqliteSession } from './session-scanner-opencode-sqlite'
|
|
import type {
|
|
OpenCodeSqliteWorkerRequest,
|
|
OpenCodeSqliteWorkerResponse
|
|
} from './session-scanner-opencode-sqlite-worker-protocol'
|
|
|
|
// Why (#8864): OpenCode SQLite reads use synchronous node:sqlite. Running them
|
|
// on this worker thread keeps the multi-GB-DB scan off the Electron main-process
|
|
// event loop. The client dispatches one request at a time, so this loop stays
|
|
// serial; imports must remain electron-free (see the worker-protocol note).
|
|
|
|
if (!parentPort) {
|
|
throw new Error('OpenCode SQLite worker must run with a parent port.')
|
|
}
|
|
const port = parentPort
|
|
|
|
async function handleRequest(
|
|
request: OpenCodeSqliteWorkerRequest
|
|
): Promise<OpenCodeSqliteWorkerResponse> {
|
|
try {
|
|
if (request.kind === 'list') {
|
|
const issues: AiVaultScanIssue[] = []
|
|
const candidates = await listOpenCodeSqliteSessions({
|
|
dbPaths: request.dbPaths,
|
|
limit: request.limit,
|
|
issues
|
|
})
|
|
return { id: request.id, ok: true, value: { candidates, issues } }
|
|
}
|
|
const session = await parseOpenCodeSqliteSession({
|
|
dbPath: request.dbPath,
|
|
sessionId: request.sessionId,
|
|
platform: request.platform
|
|
})
|
|
return { id: request.id, ok: true, value: session }
|
|
} catch (err) {
|
|
return { id: request.id, ok: false, error: err instanceof Error ? err.message : String(err) }
|
|
}
|
|
}
|
|
|
|
port.on('message', (request: OpenCodeSqliteWorkerRequest) => {
|
|
void handleRequest(request).then((response) => {
|
|
try {
|
|
port.postMessage(response)
|
|
} catch {
|
|
// A non-cloneable result would otherwise post nothing and leave the client
|
|
// waiting out its timeout; fail that request fast instead.
|
|
port.postMessage({
|
|
id: request.id,
|
|
ok: false,
|
|
error: 'OpenCode SQLite worker result could not be serialized.'
|
|
})
|
|
}
|
|
})
|
|
})
|