/** * 1M applog stress test — static query + live query incremental update. */ import { describe, it, expect } from 'vitest' import type { Applog, ApplogForInsert } from '../../applog/datom-types.ts' import { ThreadInMemory } from '../../thread/writeable.ts' import { liveQuery, query } from '../../query/basic.ts' const AGENT = 'stress-agent' function generateLarge(entityCount: number): Applog[] { const inputs: ApplogForInsert[] = [] const types = ['block', 'page', 'image', 'link', 'heading'] let ts = Date.now() - entityCount * 6 for (let i = 0; i < entityCount; i++) { const en = `e${i}` const nextTs = () => new Date(ts++).toISOString() inputs.push({ en, at: 'entity/name', vl: `Entity ${i}`, ag: AGENT, ts: nextTs() } as any) inputs.push({ en, at: 'entity/type', vl: types[i % types.length], ag: AGENT, ts: nextTs() } as any) inputs.push({ en, at: 'entity/status', vl: i % 3 === 0 ? 'active' : 'draft', ag: AGENT, ts: nextTs() } as any) if (i % 2 === 0) { inputs.push({ en, at: 'entity/content', vl: `Content ${i}`, ag: AGENT, ts: nextTs() } as any) } if (i % 4 === 0) { inputs.push({ en, at: 'relation/parent', vl: `e${Math.floor(i / 4)}`, ag: AGENT, ts: nextTs() } as any) } } // Already sorted by construction (ts is monotonically increasing) return inputs.map(i => ({ pv: null, ...i })) as Applog[] } describe('1M applog stress test', () => { let db: ReturnType it('generate ~1M applogs + load', () => { const start = performance.now() const dataset = generateLarge(200_000) const genTime = performance.now() - start const start2 = performance.now() db = ThreadInMemory.fromArray(dataset, 'stress-1m') const loadTime = performance.now() - start2 console.log(`\n [1M] Generated ${dataset.length.toLocaleString()} applogs in ${genTime.toFixed(0)}ms`) console.log(` [1M] ThreadInMemory.fromArray: ${loadTime.toFixed(0)}ms`) console.log(` [1M] Total thread size: ${db.size.toLocaleString()}`) expect(db.size).toBeGreaterThan(700_000) }) it('static query() — single step on ~750K applogs', () => { const start = performance.now() const result = query(db, [{ at: 'entity/type', vl: 'block' }]) const elapsed = performance.now() - start console.log(` [1M] query() single-step: ${elapsed.toFixed(1)}ms → ${result.nodes.length.toLocaleString()} results`) expect(result.nodes.length).toBe(40_000) // 200K / 5 types }) it('liveQuery() — setup + single insert propagation on ~750K applogs', () => { // Setup const setupStart = performance.now() const live = liveQuery(db, [{ at: 'entity/type', vl: 'block' }]) const setupTime = performance.now() - setupStart const initialCount = live.nodes.length console.log(` [1M] liveQuery() setup: ${setupTime.toFixed(1)}ms → ${initialCount.toLocaleString()} initial results`) // Insert one matching applog and measure propagation let eventTime: number | null = null live.subscribe(() => { eventTime = performance.now() }) const insertStart = performance.now() db.insert([{ en: 'new-entity-1m', at: 'entity/type', vl: 'block', ag: AGENT, }]) const totalTime = performance.now() - insertStart const subscribeDelta = eventTime ? eventTime - insertStart : null const newCount = live.nodes.length console.log(` [1M] Insert→result updated: ${totalTime.toFixed(3)}ms`) console.log(` [1M] Insert→subscribe fired: ${subscribeDelta?.toFixed(3) ?? 'N/A'}ms`) console.log(` [1M] Nodes: ${initialCount.toLocaleString()} → ${newCount.toLocaleString()}`) expect(newCount).toBe(initialCount + 1) live.dispose() }) }, { timeout: 120_000 })