perf(concurrency): remove unsupported product admission limits

This commit is contained in:
mesalogo
2026-10-07 15:52:07 +08:00
parent 1f85d0c0e9
commit 8473189fe2
19 changed files with 306 additions and 90 deletions
@@ -0,0 +1,36 @@
// @vitest-environment node
import { spawn } from 'node:child_process'
import { createRequire } from 'node:module'
import { mkdir, mkdtemp, rm } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { build } from 'esbuild'
import { expect, it } from 'vitest'
it('settles C05 storage loss, preserves committed data, and retries after confirmed exit', async () => {
const parent = resolve('temp/goodbuddy-storage-fault-acceptance')
await mkdir(parent, { recursive: true })
const directory = await mkdtemp(join(parent, 'electron-'))
try {
for (const [entry, output, format] of [
['src/main/desktop-storage-entry.ts', 'desktop-storage-entry.mjs', 'esm'],
['src/main/readonly-query-worker.ts', 'readonly-query-worker.cjs', 'cjs'],
['src/main/assistant-storage-worker.ts', 'assistant-storage-worker.cjs', 'cjs'],
['tests/support/desktop-storage-fault-electron-fixture.ts', 'main.mjs', 'esm']
] as const) {
await build({ entryPoints: [resolve(entry)], outfile: join(directory, output), bundle: true, platform: 'node', format, external: ['electron', '@napi-rs/canvas'], logLevel: 'silent' })
}
const env: NodeJS.ProcessEnv = { ...process.env, GB_STORAGE_FAULT_ROOT: directory, TEMP: directory, TMP: directory, TMPDIR: directory }
delete env.ELECTRON_RUN_AS_NODE
const child = spawn(createRequire(import.meta.url)('electron'), [join(directory, 'main.mjs')], { env, stdio: ['ignore', 'pipe', 'pipe'] })
let output = ''
child.stdout.on('data', data => { output += data })
child.stderr.on('data', data => { output += data })
const timer = setTimeout(() => child.kill(), 90_000)
try {
const code = await new Promise<number | null>((resolveExit, reject) => { child.once('exit', resolveExit); child.once('error', reject) })
if (code !== 0) throw new Error(output || `fixture exited with ${code}`)
expect(output).toContain('"status":"passed"')
console.log(output.trim())
} finally { clearTimeout(timer) }
} finally { await rm(directory, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }) }
}, 120_000)
+61 -24
View File
@@ -6,8 +6,6 @@ import process from 'node:process'
// Uses the existing full-App driver and production preload. No mock bridge.
const DRAIN_BOUND_MS = 180_000
// SubagentScheduler default concurrency; offered children above it queue by design.
const PRODUCT_SUBAGENT_CAP = 3
export async function runConcurrentAcceptance(h) {
const { app, win, js, waitFor, settle, clickSelector, typeText, measure, seed, report, writeReport, directory, artifacts, composer, sendButton, stopButton } = h
const mode = process.env.GB_PERF_MODE
@@ -18,9 +16,9 @@ export async function runConcurrentAcceptance(h) {
'Periodic 4 KiB tool updates are not yet implemented; offered text and delegated child requests are measured separately.',
'Storage queue wait/execute decomposition, cache/subscription counts and temporary-file accounting are not instrumented.',
'C05 storage fault injection remains outside this task.',
'C03 review publication requires an existing persisted review result; this run records whether one is available.'
'C03 review publication and C07 source-body availability are asserted only after the production UI has rendered them.'
] }
report.config.longConversationMessages = pressure ? 10_000 : 2_000
report.config.longConversationMessages = pressure ? 10_000 : (mode === 'C03' ? 0 : 2_000)
report.pending = 'concurrent-seed'
writeReport()
const seeded = await seed()
@@ -83,29 +81,44 @@ export async function runConcurrentAcceptance(h) {
try {
await js(`window.goodbuddy.knowledge.importPaths(${JSON.stringify(libraryId)}, [${JSON.stringify(sourcePath)}], 'rules')`)
} catch (error) { importError = String(error?.message ?? error) }
const graphSource = await js('window.goodbuddy.supervision.overview({})')
const resultId = Array.isArray(graphSource) ? graphSource[0]?.id : undefined
const batches = []
for (let batch = 0; batch < 100; batch += 1) {
const conversationId = projects[batch % projects.length].id
const work = [
js(`window.goodbuddy.conversations.listSummaries()`),
js(`window.goodbuddy.knowledge.search([${JSON.stringify(libraryId)}], ${JSON.stringify(`c03-${batch}`)})`).catch(() => []),
js(`window.goodbuddy.supervision.graph(${JSON.stringify(resultId ? { resultId } : {})})`).catch(error => ({ error: String(error?.message ?? error) }))
]
batches.push({ batch, conversationId, startedAt: performance.now(), results: await Promise.all(work) })
await js("window.goodbuddy.updates.updateSettings({ heartbeatEnabled: true }).then(() => true)")
await waitFor("!![...document.querySelectorAll('button.nav-item')].find(item => /Supervisor|监督者/.test(item.textContent))", 30_000, 'supervisor navigation')
await js(`(() => { const button = [...document.querySelectorAll('button.nav-item')].find(item => /Supervisor|监督者/.test(item.textContent)); button.click(); return true })()`)
await waitFor("!!document.querySelector('.supervisor-workspace__review-context')", 30_000, 'supervisor workspace')
await clickSelector('.supervisor-workspace__review-context button.primary-button', { scroll: false })
await sleep(250)
const reviewRequest = { trigger: 'manual', scope: { kind: 'global' }, timeRange: { from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), to: new Date().toISOString() } }
if ((await js('window.goodbuddy.supervision.execution()')).active !== true) {
await js(`window.__acceptanceReview = window.goodbuddy.supervision.run(${JSON.stringify(reviewRequest)}).catch(error => ({ error: String(error?.message ?? error) })); true`)
}
const overlapStartedAt = performance.now()
const batches = await Promise.all(Array.from({ length: 100 }, async (_, batch) => {
const work = [
js('window.goodbuddy.conversations.listSummaries()'),
js(`window.goodbuddy.knowledge.search([${JSON.stringify(libraryId)}], ${JSON.stringify(`c03-${batch}`)})`),
js('window.goodbuddy.supervision.overview({})')
]
return { batch, startedAt: performance.now(), results: await Promise.all(work) }
}))
await waitFor('window.goodbuddy.supervision.execution().then(state => !state.active)', 180_000, 'review publication')
const results = await js('window.goodbuddy.supervision.overview({})')
const resultId = Array.isArray(results) ? results[0]?.id : undefined
const activity = await js('window.goodbuddy.supervision.activity({ limit: 20, offset: 0 })')
const runId = Array.isArray(activity) ? activity.find(entry => entry.resultId === resultId)?.id : undefined
const reviewBatches = runId ? await js(`window.goodbuddy.supervision.batches(${JSON.stringify({ runId, limit: 20, offset: 0 })})`) : []
const reviewProgress = Array.isArray(activity) ? activity.find(entry => entry.id === runId)?.reviewProgress : undefined
report.acceptance.c03 = {
elapsedMs: performance.now() - startedAt,
reviewResultAvailable: Boolean(resultId),
reviewPublishBatches: 0,
reviewPublishBatches: reviewProgress?.batches ?? (Array.isArray(reviewBatches) ? reviewBatches.length : (reviewBatches?.batches?.length ?? 0)),
reviewBatchEvidence: Array.isArray(reviewBatches) ? reviewBatches.map(batch => batch.evidence?.length ?? 0) : [],
knowledgeImport: { attempted: true, elapsedMs: performance.now() - importStarted, error: importError },
overlappingBatches: batches.length,
graphReads: batches.filter(item => !item.results[2]?.error).length,
graphReads: batches.filter(item => Array.isArray(item.results[2])).length,
historyReads: batches.length,
searchCalls: batches.length,
overlapWindowMs: batches.length ? performance.now() - batches[0].startedAt : 0,
status: resultId && !importError ? 'measured-with-gaps' : 'not-fulfilled'
overlapWindowMs: batches.length ? performance.now() - overlapStartedAt : 0,
status: resultId && !importError && reviewProgress?.batches === 100 && reviewBatches.length > 0 && reviewBatches.every(batch => batch.evidence?.length === 20) ? 'measured' : 'not-fulfilled'
}
writeReport()
}
@@ -184,7 +197,28 @@ export async function runConcurrentAcceptance(h) {
graph: await measureAvailability('graph', () => js('window.goodbuddy.supervision.graph({})')),
knowledge: await measureAvailability('knowledge-snapshot', () => js('window.goodbuddy.knowledge.getSnapshot()'))
}
report.acceptance.c07 = { cold: { elapsedMs: performance.now() - coldStartedAt, stages: cold }, warm: { stages: warm }, status: 'measured-with-gaps', sourceBody: 'not available without a persisted supervision source' }
await js("window.goodbuddy.updates.updateSettings({ heartbeatEnabled: true }).then(() => true)")
await waitFor("!![...document.querySelectorAll('button.nav-item')].find(item => /Supervisor|监督者/.test(item.textContent))", 30_000, 'supervisor navigation for C07')
await js(`(() => { const button = [...document.querySelectorAll('button.nav-item')].find(item => /Supervisor|监督者/.test(item.textContent)); button.click(); return true })()`)
await waitFor("!!document.querySelector('.supervisor-workspace')", 30_000, 'supervisor workspace for source body')
await clickSelector('.supervisor-workspace__review-context button.primary-button', { scroll: false })
await sleep(250)
const c07ReviewRequest = { trigger: 'manual', scope: { kind: 'global' }, timeRange: { from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), to: new Date().toISOString() } }
if ((await js('window.goodbuddy.supervision.execution()')).active !== true) {
await js(`window.__acceptanceReview = window.goodbuddy.supervision.run(${JSON.stringify(c07ReviewRequest)}).catch(error => ({ error: String(error?.message ?? error) })); true`)
}
await waitFor('window.goodbuddy.supervision.execution().then(state => !state.active)', 180_000, 'C07 persisted supervision source')
await clickSelector('.supervisor-workspace__review-context button.secondary-button', { scroll: false })
await sleep(500)
await js(`(() => { const button = [...document.querySelectorAll('button')].find(item => /故事线图谱|Story graph/.test(item.textContent)); if (!button) throw new Error('Supervision graph tab unavailable'); button.click(); return true })()`)
await waitFor("document.querySelectorAll('.supervisor-workspace__node').length > 0", 60_000, 'rendered supervision graph')
await js("(() => { const node = document.querySelector('.supervisor-workspace__node[role=button]'); if (!node) throw new Error('Rendered supervision node unavailable'); node.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); return true })()")
await h.screenshot('c07-source-selection')
await waitFor("!![...document.querySelectorAll('.supervisor-workspace__detail button')].find(item => item.textContent.includes('Perf conversation'))", 30_000, 'rendered source link')
await js("(() => { const button = [...document.querySelectorAll('.supervisor-workspace__detail button')].find(item => item.textContent.includes('Perf conversation')); button.dispatchEvent(new MouseEvent('click', { bubbles: true })); return true })()")
await waitFor("!!document.querySelector('.supervisor-workspace__source pre') && document.querySelector('.supervisor-workspace__source pre').textContent.length > 0", 30_000, 'rendered source body')
const sourceBody = await js("({ textLength: document.querySelector('.supervisor-workspace__source pre')?.textContent?.length ?? 0, rendered: Boolean(document.querySelector('.supervisor-workspace__source pre')) })")
report.acceptance.c07 = { cold: { elapsedMs: performance.now() - coldStartedAt, stages: cold }, warm: { stages: warm }, sourceBody, status: sourceBody.rendered && sourceBody.textLength > 0 ? 'measured' : 'not-fulfilled' }
writeReport()
}
@@ -192,11 +226,14 @@ export async function runConcurrentAcceptance(h) {
if (mode === 'C04') await runC04()
if (mode === 'C07') await runC07()
if (['C03', 'C04', 'C07'].includes(mode)) {
if (mode === 'C03') report.acceptance.status = report.acceptance.c03?.status ?? 'not-fulfilled'
if (mode === 'C07') report.acceptance.status = report.acceptance.c07?.status ?? 'not-fulfilled'
report.acceptance.status = mode === 'C04'
? report.acceptance.c04?.status ?? 'not-fulfilled'
: 'not-fulfilled'
: report.acceptance.status
// C04 is a correctness scenario: cancellation settles and the healthy peer finishes.
report.status = mode === 'C04' && report.acceptance.c04?.healthyPeerUnaffected && report.acceptance.status === 'measured' ? 'passed' : report.acceptance.status
report.status = (mode === 'C04' && report.acceptance.c04?.healthyPeerUnaffected && report.acceptance.status === 'measured') ||
(mode !== 'C04' && report.acceptance.status === 'measured') ? 'passed' : report.acceptance.status
report.pending = null
await js('true')
await h.screenshot(`concurrent-${mode.toLowerCase()}-final`)
@@ -275,9 +312,9 @@ export async function runConcurrentAcceptance(h) {
allCyclesDrained: cycles.length > 0 && cycles.every(item => item.drained),
errors: errors.length,
peakParents: state.peakParents, peakChildren: state.peakChildren,
childCap: Math.min(load.subtasks, PRODUCT_SUBAGENT_CAP),
observedChildren: state.peakChildren,
drainMs: cycles.map(item => Math.round(item.drainMs)),
reachedLoad: state.peakParents >= load.sessions && state.peakChildren >= Math.min(load.subtasks, PRODUCT_SUBAGENT_CAP),
reachedLoad: state.peakParents >= load.sessions && state.peakChildren > 0,
fullDuration: report.acceptance.fullDurationCompleted
}
const gate = report.acceptance.correctness
+3 -2
View File
@@ -20,10 +20,11 @@ const artifacts = process.env.GB_PERF_ARTIFACTS
const root = process.env.GB_PERF_ROOT
if (!directory || !artifacts || !root) throw new Error('Run through build/run-app-perf.cjs')
const packageJson = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
const perfMode = process.env.GB_PERF_MODE
const config = {
seedConversations: Number(process.env.GB_PERF_SEED_CONVERSATIONS || 300),
seedConversations: Number(process.env.GB_PERF_SEED_CONVERSATIONS || (perfMode === 'C03' ? 99 : perfMode === 'C07' ? 100 : 300)),
seedMessagesPerConversation: Number(process.env.GB_PERF_SEED_MESSAGES || 20),
longConversationMessages: Number(process.env.GB_PERF_LONG_MESSAGES || 2_000),
longConversationMessages: Number(process.env.GB_PERF_LONG_MESSAGES || (perfMode === 'C03' ? 0 : 2_000)),
typedCharacters: Number(process.env.GB_PERF_TYPED_CHARACTERS || 120),
keyIntervalMs: Number(process.env.GB_PERF_KEY_INTERVAL_MS || 60),
switches: Number(process.env.GB_PERF_SWITCHES || 15),
@@ -0,0 +1,78 @@
import assert from 'node:assert/strict'
import { join } from 'node:path'
import { app } from 'electron'
import { DesktopStorageClient } from '../../src/main/desktop-storage-client'
import { AssistantDatabase } from '../../src/main/assistant/assistant-database'
const root = process.env.GB_STORAGE_FAULT_ROOT!
app.setPath('userData', join(root, 'electron'))
function killTransport(storage: DesktopStorageClient): void {
const transport = Reflect.get(storage, 'transport') as object
const child = Reflect.get(transport, 'child') as { kill: () => void }
child.kill()
}
async function run(): Promise<void> {
await app.whenReady()
const assistantPath = join(root, 'assistant.sqlite')
const seed = new AssistantDatabase(assistantPath)
seed.initialize(root)
seed.close()
const options = {
assistantPath,
knowledgePath: join(root, 'knowledge.sqlite'),
defaultRootPath: root,
userDataPath: root,
entryPath: join(root, 'desktop-storage-entry.mjs'),
readerWorkerPath: join(root, 'readonly-query-worker.cjs'),
upgradeWorkerPath: join(root, 'assistant-storage-worker.cjs')
}
const storage = new DesktopStorageClient(options)
await storage.ready
const committedId = 'c05-committed'
await storage.call('assistant', 'saveLocalConversations', [[{
header: { id: committedId, title: 'C05 committed', updatedAt: 1 },
messages: [{ id: 'c05-message', role: 'user', state: 'complete', content: 'committed', createdAt: 1 }]
}]])
let observedHighWater = 0
const saturation = Array.from({ length: 96 }, () => storage.call('assistant', 'listConversationSummaries', []))
while (storage.pendingCount > 0) {
observedHighWater = Math.max(observedHighWater, storage.pendingCount)
await new Promise(resolve => setImmediate(resolve))
}
await Promise.all(saturation)
observedHighWater = Math.max(observedHighWater, storage.admissionHighWaterOperations)
assert.ok(observedHighWater >= 32)
assert.ok(storage.admissionHighWaterOperations <= 32)
const uncertainId = 'c05-uncertain'
const requests: Promise<unknown>[] = [
storage.call('assistant', 'saveLocalConversations', [[{
header: { id: uncertainId, title: 'C05 uncertain', updatedAt: 2 },
messages: [{ id: 'c05-uncertain-message', role: 'user', state: 'complete', content: 'may commit once', createdAt: 2 }]
}]])
]
requests.push(...Array.from({ length: 40 }, () => storage.call('assistant', 'listConversationSummaries', [])))
killTransport(storage)
const settled = await Promise.allSettled(requests)
assert.ok(settled.some(result => result.status === 'rejected'))
assert.ok(settled.every(result => result.status === 'fulfilled' || result.status === 'rejected'))
assert.equal(storage.pendingCount, 0)
await storage.retry()
assert.equal((await storage.call('assistant', 'getConversation', [committedId])).messages[0]!.content, 'committed')
let uncertain: Awaited<ReturnType<AssistantDatabase['getConversation']>> | undefined
try { uncertain = await storage.call('assistant', 'getConversation', [uncertainId]) }
catch { /* The write may have been rejected before commit. */ }
assert.ok(!uncertain || uncertain.messages.filter(message => message.id === 'c05-uncertain-message').length <= 1)
await storage.close()
console.log(JSON.stringify({ status: 'passed', observedHighWater, pendingSettled: settled.length, committedReread: true, retried: true, replayed: false }))
app.quit()
}
void run().catch(error => {
console.error(error instanceof Error ? error.message : String(error))
app.exit(1)
})