200 lines
7.7 KiB
TypeScript
200 lines
7.7 KiB
TypeScript
|
|
/**
|
||
|
|
* OPEN-PATH tests: init() must never gate on the embedding model, the VFS
|
||
|
|
* root bootstrap must never touch the embedding engine, and a slow open
|
||
|
|
* must narrate its phases.
|
||
|
|
*
|
||
|
|
* Background: a production restart storm measured 90,017ms for a single
|
||
|
|
* brain init vs 1,117ms quiet — an ~80x contention multiplier — traced to
|
||
|
|
* every writer's init() eagerly awaiting the process-global WASM embedding
|
||
|
|
* engine before the VFS root even existed. See src/brainy.ts performInit()
|
||
|
|
* and src/vfs/VirtualFileSystem.ts doInitializeRoot().
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { describe, it, expect, vi } from 'vitest'
|
||
|
|
import { Brainy } from '../../../src/brainy'
|
||
|
|
import { MemoryStorage } from '../../../src/storage/adapters/memoryStorage'
|
||
|
|
import { embeddingManager } from '../../../src/embeddings/EmbeddingManager'
|
||
|
|
import { createTestConfig } from '../../helpers/test-factory'
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The four signals `isDeterministicEmbedMode()` checks (see
|
||
|
|
* src/embeddings/deterministicEmbedMode.ts). The global unit-test setup
|
||
|
|
* (tests/setup-unit.ts) sets some of these for the whole file/run in some
|
||
|
|
* vitest configurations; other configurations leave them unset and run the
|
||
|
|
* real WASM engine instead. The background-warm tests below need the
|
||
|
|
* "not unit-test mode" branch of performInit() to actually execute, so they
|
||
|
|
* save/clear/restore all four explicitly — deterministic regardless of
|
||
|
|
* which config invoked this file, never relying on ambient state.
|
||
|
|
*/
|
||
|
|
function withRealEmbedderBranch<T>(fn: () => Promise<T>): Promise<T> {
|
||
|
|
const savedEnvDeterministic = process.env.BRAINY_DETERMINISTIC_EMBEDDINGS
|
||
|
|
const savedEnvUnitTest = process.env.BRAINY_UNIT_TEST
|
||
|
|
const g = globalThis as Record<string, unknown>
|
||
|
|
const savedGlobalDeterministic = g.__BRAINY_DETERMINISTIC_EMBED__
|
||
|
|
const savedGlobalUnitTest = g.__BRAINY_UNIT_TEST__
|
||
|
|
|
||
|
|
delete process.env.BRAINY_DETERMINISTIC_EMBEDDINGS
|
||
|
|
delete process.env.BRAINY_UNIT_TEST
|
||
|
|
delete g.__BRAINY_DETERMINISTIC_EMBED__
|
||
|
|
delete g.__BRAINY_UNIT_TEST__
|
||
|
|
|
||
|
|
const restore = () => {
|
||
|
|
if (savedEnvDeterministic !== undefined) process.env.BRAINY_DETERMINISTIC_EMBEDDINGS = savedEnvDeterministic
|
||
|
|
if (savedEnvUnitTest !== undefined) process.env.BRAINY_UNIT_TEST = savedEnvUnitTest
|
||
|
|
if (savedGlobalDeterministic !== undefined) g.__BRAINY_DETERMINISTIC_EMBED__ = savedGlobalDeterministic
|
||
|
|
if (savedGlobalUnitTest !== undefined) g.__BRAINY_UNIT_TEST__ = savedGlobalUnitTest
|
||
|
|
}
|
||
|
|
|
||
|
|
return fn().finally(restore)
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A MemoryStorage whose init() takes an artificially long time — a
|
||
|
|
* controllable fake seam (not a wall-clock race) that reliably pushes
|
||
|
|
* performInit()'s "storage-init" phase (and therefore the total open time)
|
||
|
|
* past the 2000ms narration threshold, without touching the filesystem or
|
||
|
|
* relying on real contention.
|
||
|
|
*/
|
||
|
|
class SlowMemoryStorage extends MemoryStorage {
|
||
|
|
override async init(): Promise<void> {
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 2200))
|
||
|
|
await super.init()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('OPEN-PATH: init() never gates on the embedding model', () => {
|
||
|
|
it('bootstrapping a fresh store never calls the embedding engine (VFS root add is engine-untouched)', async () => {
|
||
|
|
const embedSpy = vi.spyOn(embeddingManager, 'embed')
|
||
|
|
const brain = new Brainy(createTestConfig())
|
||
|
|
try {
|
||
|
|
await brain.init()
|
||
|
|
|
||
|
|
// The VFS root's add() must never have reached the embedding engine —
|
||
|
|
// it carries an explicit placeholder vector instead (see
|
||
|
|
// VirtualFileSystem.doInitializeRoot()).
|
||
|
|
expect(embedSpy).not.toHaveBeenCalled()
|
||
|
|
|
||
|
|
// Sanity: the VFS is genuinely usable afterwards.
|
||
|
|
const files = await brain.vfs.readdir('/')
|
||
|
|
expect(files).toEqual([])
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
embedSpy.mockRestore()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
it('starts the embedding-engine warm in the BACKGROUND — init() resolves before the warm does', async () => {
|
||
|
|
await withRealEmbedderBranch(async () => {
|
||
|
|
const events: string[] = []
|
||
|
|
let releaseWarm!: () => void
|
||
|
|
const warmGate = new Promise<void>((resolve) => {
|
||
|
|
releaseWarm = resolve
|
||
|
|
})
|
||
|
|
|
||
|
|
const initSpy = vi.spyOn(embeddingManager, 'init').mockImplementation(async () => {
|
||
|
|
events.push('warm-start')
|
||
|
|
await warmGate
|
||
|
|
events.push('warm-resolve')
|
||
|
|
})
|
||
|
|
|
||
|
|
const brain = new Brainy(createTestConfig())
|
||
|
|
try {
|
||
|
|
await brain.init()
|
||
|
|
events.push('init-resolved')
|
||
|
|
|
||
|
|
// init() started the warm but returned WITHOUT waiting for it.
|
||
|
|
expect(initSpy).toHaveBeenCalledTimes(1)
|
||
|
|
expect(events).toEqual(['warm-start', 'init-resolved'])
|
||
|
|
|
||
|
|
// Now let the fake warm finish and confirm it lands strictly after.
|
||
|
|
releaseWarm()
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
||
|
|
expect(events).toEqual(['warm-start', 'init-resolved', 'warm-resolve'])
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
initSpy.mockRestore()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
it('narrates a background warm FAILURE loudly instead of losing it silently', async () => {
|
||
|
|
await withRealEmbedderBranch(async () => {
|
||
|
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||
|
|
const initSpy = vi
|
||
|
|
.spyOn(embeddingManager, 'init')
|
||
|
|
.mockRejectedValue(new Error('simulated cold-compile failure'))
|
||
|
|
|
||
|
|
const brain = new Brainy(createTestConfig())
|
||
|
|
try {
|
||
|
|
// init() itself must still resolve — a failed background warm is
|
||
|
|
// never fatal to open().
|
||
|
|
await expect(brain.init()).resolves.toBeUndefined()
|
||
|
|
|
||
|
|
// Give the background .catch() a microtask/macrotask to run.
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
||
|
|
|
||
|
|
const failureLine = warnSpy.mock.calls
|
||
|
|
.map((args) => args.map(String).join(' '))
|
||
|
|
.find((line) => line.includes('background embedding-engine warm FAILED'))
|
||
|
|
expect(failureLine).toBeDefined()
|
||
|
|
expect(failureLine).toContain('simulated cold-compile failure')
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
initSpy.mockRestore()
|
||
|
|
warnSpy.mockRestore()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
it('eagerEmbeddings: false starts no warm at all', async () => {
|
||
|
|
await withRealEmbedderBranch(async () => {
|
||
|
|
const initSpy = vi.spyOn(embeddingManager, 'init')
|
||
|
|
const brain = new Brainy({ ...createTestConfig(), eagerEmbeddings: false })
|
||
|
|
try {
|
||
|
|
await brain.init()
|
||
|
|
expect(initSpy).not.toHaveBeenCalled()
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
initSpy.mockRestore()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
it('narrates a slow open with a per-phase ms breakdown once total time exceeds 2000ms', async () => {
|
||
|
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||
|
|
const brain = new Brainy({ ...createTestConfig(), storage: new SlowMemoryStorage() })
|
||
|
|
try {
|
||
|
|
await brain.init()
|
||
|
|
|
||
|
|
const slowOpenLine = warnSpy.mock.calls
|
||
|
|
.map((args) => args.map(String).join(' '))
|
||
|
|
.find((line) => line.includes('[Brainy] slow open:'))
|
||
|
|
|
||
|
|
expect(slowOpenLine).toBeDefined()
|
||
|
|
expect(slowOpenLine).toContain('storage-init=')
|
||
|
|
expect(slowOpenLine).toContain('generation-store-open-fold=')
|
||
|
|
expect(slowOpenLine).toContain('index-init-gate=')
|
||
|
|
expect(slowOpenLine).toContain('vfs-bootstrap=')
|
||
|
|
expect(slowOpenLine).toContain('embedding-warm-started=')
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
warnSpy.mockRestore()
|
||
|
|
}
|
||
|
|
}, 20000)
|
||
|
|
|
||
|
|
it('stays silent about phase timing when open is fast (under 2000ms)', async () => {
|
||
|
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||
|
|
const brain = new Brainy(createTestConfig())
|
||
|
|
try {
|
||
|
|
await brain.init()
|
||
|
|
const slowOpenLine = warnSpy.mock.calls
|
||
|
|
.map((args) => args.map(String).join(' '))
|
||
|
|
.find((line) => line.includes('[Brainy] slow open:'))
|
||
|
|
expect(slowOpenLine).toBeUndefined()
|
||
|
|
} finally {
|
||
|
|
await brain.close()
|
||
|
|
warnSpy.mockRestore()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|