mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 16:02:11 +00:00
* fix(opencode): read the binder's session store on the foreign SQLite reader worker (STA-9122) Before: the OpenCode session binder listed new sessions from opencode.db with node:sqlite on the main thread every 60 s (and on SessionStart kicks), so a large or contended store could stall the app the same way Cursor's did. After: the read is a pure openCodeBinderSessions reader in foreign-sqlite-readers/readers/, run only on the worker. The binder's correlation, pane snapshot and process sweep stay where they were. - The binder round awaits listSessions and re-checks its generation right after, so a stop() during the read discards the round before it touches the unbound map or the watermark. - The client's in-flight dedupe key now includes the cursor, so a stale round from before a restart cannot hand its rows to the restarted round. - Idle teardown is per reader. The binder lane keeps its thread for 120 s, longer than its 60 s poll, so the thread is not respawned every round. - A timeout, crash, malformed reply or unstartable worker resolves to [] (no sessions), the value the old read already returned on failure. - An absent store still reads as [] without a log line, and a permission or corrupt-file failure still logs (kept from #24577, now in the reader: it stats the path and throws anything but ENOENT/ENOTDIR to the client's log). - The binder lane inherits #24572's limits from the shared lane: no respawn until a timed-out worker has exited, 2 consecutive deaths, a queue cap of 8. Its timeout stays 60 s, matching its poll. - dispatch switches on the destructured kind, so a new kind without a case still fails to compile. orcad: the hook server runs there too, so orcad now ships foreign-sqlite-reader-entry.js beside orcad.js (ORCAD_ARTIFACTS, built as an orcad child). build-orcad runs a smoke check that starts the built worker under the build's Node and under the pinned runtime, and does a real binder read on a fixture DB, a Cursor read of a missing file and an OpenCode history list. The OpenCode history scanner uses the same entry and was bundled into orcad without it, so on orcad it always failed closed; it can now run. Tests: reader (cursor, same-ms ids, OpenCode 2 rows, missing then created, corrupt, inaccessible directory), retirement gate for the binder lane, dispatch routing, client lane (rows, failure -> [], dedupe per cursor, own thread, idle teardown default and override), binder loop with an async listSessions (failure -> [], stop during the read), orcad path resolution through orcad's host adapters, artifact list, and the smoke check against good, missing and non-reading entries. * test(opencode): cover the binder read deadline with fake timers and name the failure test accurately (STA-9122)
66 lines
2.2 KiB
JavaScript
66 lines
2.2 KiB
JavaScript
import { build } from 'esbuild'
|
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join, resolve } from 'node:path'
|
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
import { smokeForeignSqliteReaderWorker } from './foreign-sqlite-reader-worker-smoke.mjs'
|
|
import { ORCAD_CHILD_ENTRY_POINTS } from './orcad-entry-build.mjs'
|
|
|
|
const ENTRY = 'foreign-sqlite-reader-entry.js'
|
|
const directories = []
|
|
let builtDirectory
|
|
|
|
function fixtureDirectory() {
|
|
const directory = mkdtempSync(join(tmpdir(), 'orca-foreign-sqlite-smoke-test-'))
|
|
directories.push(directory)
|
|
return directory
|
|
}
|
|
|
|
beforeAll(async () => {
|
|
builtDirectory = fixtureDirectory()
|
|
await build({
|
|
entryPoints: [resolve(ORCAD_CHILD_ENTRY_POINTS.foreignSqliteReader)],
|
|
outfile: join(builtDirectory, ENTRY),
|
|
bundle: true,
|
|
platform: 'node',
|
|
target: 'node18',
|
|
format: 'cjs',
|
|
external: ['electron'],
|
|
logLevel: 'silent'
|
|
})
|
|
}, 60_000)
|
|
|
|
afterAll(() => {
|
|
for (const directory of directories) {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
describe('foreign SQLite reader build smoke', () => {
|
|
it('passes against the built entry', () => {
|
|
expect(() => smokeForeignSqliteReaderWorker(builtDirectory)).not.toThrow()
|
|
})
|
|
|
|
it('fails when the entry is missing', () => {
|
|
expect(() => smokeForeignSqliteReaderWorker(fixtureDirectory())).toThrow('smoke failed')
|
|
})
|
|
|
|
it('fails when the worker answers without reading', () => {
|
|
const directory = fixtureDirectory()
|
|
writeFileSync(
|
|
join(directory, ENTRY),
|
|
`const { parentPort } = require('node:worker_threads')
|
|
parentPort.on('message', ({ id }) => parentPort.postMessage({ id, ok: true, value: [] }))`
|
|
)
|
|
expect(() => smokeForeignSqliteReaderWorker(directory)).toThrow('smoke failed')
|
|
})
|
|
|
|
it('runs in the orcad build under both runtimes', () => {
|
|
const source = readFileSync(resolve('config/scripts/build-orcad.mjs'), 'utf8')
|
|
expect(source).toContain('smokeForeignSqliteReaderWorker(OUT_DIR)')
|
|
expect(source).toContain(
|
|
'smokeForeignSqliteReaderWorker(OUT_DIR, { runtimePath: nodeRuntimePath })'
|
|
)
|
|
})
|
|
})
|