mirror of
https://github.com/adnaan-worker/adnify.git
synced 2026-09-01 15:12:10 +08:00
231441865e
test: enhance indexServiceScheduling tests with structural watcher updates test: extend structuralIndexStore tests for batch processing and metadata storage test: add sessionSqliteStore tests for clean shutdowns and blob management
185 lines
6.0 KiB
JavaScript
185 lines
6.0 KiB
JavaScript
const { Worker } = require('node:worker_threads')
|
|
const { mkdtemp, rm, stat } = require('node:fs/promises')
|
|
const { tmpdir } = require('node:os')
|
|
const path = require('node:path')
|
|
const { performance } = require('node:perf_hooks')
|
|
const { randomUUID } = require('node:crypto')
|
|
|
|
function createClient(workerPath) {
|
|
const worker = new Worker(workerPath)
|
|
const pending = new Map()
|
|
worker.on('message', response => {
|
|
const request = pending.get(response.requestId)
|
|
if (!request) return
|
|
pending.delete(response.requestId)
|
|
if (response.ok) request.resolve(response.result)
|
|
else request.reject(new Error(response.error))
|
|
})
|
|
worker.on('error', error => {
|
|
for (const request of pending.values()) request.reject(error)
|
|
pending.clear()
|
|
})
|
|
return {
|
|
request(operation) {
|
|
const requestId = randomUUID()
|
|
return new Promise((resolve, reject) => {
|
|
pending.set(requestId, { resolve, reject })
|
|
worker.postMessage({ requestId, operation })
|
|
})
|
|
},
|
|
terminate: () => worker.terminate(),
|
|
}
|
|
}
|
|
|
|
async function fileBytes(filePath) {
|
|
return stat(filePath).then(value => value.size).catch(() => 0)
|
|
}
|
|
|
|
async function benchmarkStructural(root) {
|
|
const workerPath = path.resolve('dist/main/structuralIndexStore.worker.js')
|
|
const databasePath = path.join(root, 'structural.sqlite')
|
|
const client = createClient(workerPath)
|
|
const generation = randomUUID()
|
|
const fileCount = 2_000
|
|
const chunkCount = 10_000
|
|
const chunks = Array.from({ length: chunkCount }, (_, index) => {
|
|
const fileIndex = Math.floor(index / 5)
|
|
const relativePath = `src/file-${fileIndex}.ts`
|
|
return {
|
|
id: `chunk-${index}`,
|
|
filePath: path.join(root, relativePath),
|
|
relativePath,
|
|
fileHash: `hash-${fileIndex}`,
|
|
content: `export function symbol${index}() { return ${index} }`,
|
|
startLine: index % 5,
|
|
endLine: index % 5,
|
|
type: 'function',
|
|
language: 'typescript',
|
|
symbols: [`symbol${index}`],
|
|
}
|
|
})
|
|
|
|
const writeStart = performance.now()
|
|
await client.request({ type: 'beginReplace', databasePath, generation })
|
|
for (let offset = 0; offset < chunks.length; offset += 512) {
|
|
await client.request({
|
|
type: 'appendReplace', databasePath, generation,
|
|
chunks: chunks.slice(offset, offset + 512),
|
|
})
|
|
}
|
|
await client.request({
|
|
type: 'commitReplace', databasePath, generation,
|
|
metadata: { totalFiles: fileCount, totalChunks: chunkCount, savedAt: Date.now() },
|
|
})
|
|
const writeMs = performance.now() - writeStart
|
|
|
|
const readStart = performance.now()
|
|
let cursor
|
|
let loaded = 0
|
|
let pages = 0
|
|
do {
|
|
const result = await client.request({ type: 'loadPage', databasePath, cursor })
|
|
loaded += result.chunks.length
|
|
pages += 1
|
|
cursor = result.nextCursor || undefined
|
|
} while (cursor)
|
|
const readMs = performance.now() - readStart
|
|
await client.request({ type: 'close', databasePath })
|
|
await client.terminate()
|
|
|
|
return {
|
|
files: fileCount,
|
|
chunks: loaded,
|
|
pages,
|
|
writeMs: Math.round(writeMs),
|
|
readMs: Math.round(readMs),
|
|
databaseBytes: await fileBytes(databasePath),
|
|
}
|
|
}
|
|
|
|
async function benchmarkSessions(root) {
|
|
const workerPath = path.resolve('dist/main/sessionStorage.worker.js')
|
|
const databasePath = path.join(root, 'sessions.sqlite3')
|
|
const client = createClient(workerPath)
|
|
const threadCount = 100
|
|
const messagesPerThread = 100
|
|
const payloadText = 'session-payload-'.repeat(32)
|
|
const threads = Array.from({ length: threadCount }, (_, threadIndex) => ({
|
|
metadata: {
|
|
id: `thread-${threadIndex}`,
|
|
createdAt: threadIndex,
|
|
lastModified: threadIndex,
|
|
title: `Thread ${threadIndex}`,
|
|
messageCount: messagesPerThread,
|
|
data: {},
|
|
},
|
|
replaceFrom: 0,
|
|
messages: Array.from({ length: messagesPerThread }, (_, ordinal) => ({
|
|
ordinal,
|
|
id: `message-${threadIndex}-${ordinal}`,
|
|
role: ordinal % 2 === 0 ? 'user' : 'assistant',
|
|
timestamp: ordinal,
|
|
payload: { id: `message-${threadIndex}-${ordinal}`, content: payloadText },
|
|
})),
|
|
}))
|
|
|
|
await client.request({ type: 'open', databasePath })
|
|
const writeStart = performance.now()
|
|
await client.request({
|
|
type: 'applyPatch', databasePath,
|
|
patch: { threads, deletedThreadIds: [], branchThreads: [] },
|
|
})
|
|
const writeMs = performance.now() - writeStart
|
|
|
|
const catalogStart = performance.now()
|
|
await client.request({ type: 'loadCatalog', databasePath })
|
|
const catalogMs = performance.now() - catalogStart
|
|
const threadStart = performance.now()
|
|
await client.request({ type: 'loadMessages', databasePath, threadId: 'thread-50' })
|
|
const threadMs = performance.now() - threadStart
|
|
|
|
const tailStart = performance.now()
|
|
await client.request({
|
|
type: 'applyPatch', databasePath,
|
|
patch: {
|
|
deletedThreadIds: [], branchThreads: [],
|
|
threads: [{
|
|
metadata: { ...threads[50].metadata, lastModified: Date.now() },
|
|
replaceFrom: 99,
|
|
messages: [{ ...threads[50].messages[99], payload: { id: 'tail', content: 'updated' } }],
|
|
}],
|
|
},
|
|
})
|
|
const tailWriteMs = performance.now() - tailStart
|
|
const stats = await client.request({ type: 'getStats', databasePath })
|
|
await client.request({ type: 'closeAll' })
|
|
await client.terminate()
|
|
|
|
return {
|
|
threads: threadCount,
|
|
messages: threadCount * messagesPerThread,
|
|
writeMs: Math.round(writeMs),
|
|
catalogMs: Math.round(catalogMs),
|
|
activeThreadMs: Math.round(threadMs),
|
|
tailWriteMs: Math.round(tailWriteMs),
|
|
databaseBytes: stats.stats.databaseBytes,
|
|
walBytesBeforeClose: stats.stats.walBytes,
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const root = await mkdtemp(path.join(tmpdir(), 'adnify-sqlite-benchmark-'))
|
|
try {
|
|
const structural = await benchmarkStructural(root)
|
|
const sessions = await benchmarkSessions(root)
|
|
process.stdout.write(`${JSON.stringify({ structural, sessions }, null, 2)}\n`)
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
main().catch(error => {
|
|
process.stderr.write(`${error.stack || error}\n`)
|
|
process.exitCode = 1
|
|
})
|