| |
| |
| import fs from "node:fs"; |
| import os from "node:os"; |
| import path from "node:path"; |
| import { describe, it, beforeAll, afterAll, vi } from "vitest"; |
|
|
| const N_ITEMS = 500; |
| const N_QUERIES = 200; |
|
|
| const originalDataDir = process.env.DATA_DIR; |
| let tempSqlite, tempLowdb; |
| let sqliteDb, lowDb; |
|
|
| function fmt(ms) { return `${ms.toFixed(2)}ms`; } |
|
|
| async function bench(label, fn) { |
| |
| await fn(); |
| const t0 = performance.now(); |
| await fn(); |
| const dt = performance.now() - t0; |
| console.log(` ${label.padEnd(40)} ${fmt(dt)}`); |
| return dt; |
| } |
|
|
| beforeAll(async () => { |
| |
| tempSqlite = fs.mkdtempSync(path.join(os.tmpdir(), "9router-bench-sqlite-")); |
| process.env.DATA_DIR = tempSqlite; |
| vi.resetModules(); |
| sqliteDb = await import("@/lib/db/index.js"); |
| await sqliteDb.initDb(); |
|
|
| |
| tempLowdb = fs.mkdtempSync(path.join(os.tmpdir(), "9router-bench-lowdb-")); |
| const { Low } = await import("lowdb"); |
| const { JSONFile } = await import("lowdb/node"); |
| const dbFile = path.join(tempLowdb, "db.json"); |
| fs.writeFileSync(dbFile, JSON.stringify({ providerConnections: [], usageHistory: [] })); |
| lowDb = new Low(new JSONFile(dbFile), { providerConnections: [], usageHistory: [] }); |
| await lowDb.read(); |
| }); |
|
|
| afterAll(() => { |
| if (tempSqlite) fs.rmSync(tempSqlite, { recursive: true, force: true }); |
| if (tempLowdb) fs.rmSync(tempLowdb, { recursive: true, force: true }); |
| if (originalDataDir === undefined) delete process.env.DATA_DIR; |
| else process.env.DATA_DIR = originalDataDir; |
| }); |
|
|
| describe("DB Benchmark β SQLite vs Lowdb", () => { |
| it(`INSERT ${N_ITEMS} provider connections`, async () => { |
| console.log(`\n[INSERT ${N_ITEMS}]`); |
|
|
| const sqliteTime = await bench("SQLite createProviderConnection", async () => { |
| for (let i = 0; i < N_ITEMS; i++) { |
| await sqliteDb.createProviderConnection({ |
| provider: `bench-p${i % 5}`, authType: "apikey", |
| name: `name-${i}`, apiKey: `k-${i}`, |
| }); |
| } |
| }); |
|
|
| const lowdbTime = await bench("Lowdb push + write", async () => { |
| for (let i = 0; i < N_ITEMS; i++) { |
| lowDb.data.providerConnections.push({ |
| id: `id-${i}`, provider: `bench-p${i % 5}`, authType: "apikey", |
| name: `name-${i}`, apiKey: `k-${i}`, priority: i + 1, isActive: true, |
| createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), |
| }); |
| await lowDb.write(); |
| } |
| }); |
|
|
| const speedup = (lowdbTime / sqliteTime).toFixed(2); |
| console.log(` β SQLite is ${speedup}x faster`); |
| }, 60000); |
|
|
| it(`READ ${N_QUERIES} filtered queries`, async () => { |
| console.log(`\n[READ ${N_QUERIES} filtered queries]`); |
|
|
| const sqliteTime = await bench("SQLite getProviderConnections(filter)", async () => { |
| for (let i = 0; i < N_QUERIES; i++) { |
| await sqliteDb.getProviderConnections({ provider: `bench-p${i % 5}` }); |
| } |
| }); |
|
|
| const lowdbTime = await bench("Lowdb read + filter", async () => { |
| for (let i = 0; i < N_QUERIES; i++) { |
| await lowDb.read(); |
| lowDb.data.providerConnections.filter((c) => c.provider === `bench-p${i % 5}`); |
| } |
| }); |
|
|
| const speedup = (lowdbTime / sqliteTime).toFixed(2); |
| console.log(` β SQLite is ${speedup}x faster`); |
| }, 60000); |
|
|
| it(`READ ${N_QUERIES} by id (point lookup)`, async () => { |
| console.log(`\n[READ ${N_QUERIES} by id]`); |
|
|
| const sqliteAll = await sqliteDb.getProviderConnections(); |
| const ids = sqliteAll.slice(0, N_QUERIES).map((c) => c.id); |
|
|
| const sqliteTime = await bench("SQLite getProviderConnectionById", async () => { |
| for (const id of ids) await sqliteDb.getProviderConnectionById(id); |
| }); |
|
|
| const lowdbIds = lowDb.data.providerConnections.slice(0, N_QUERIES).map((c) => c.id); |
| const lowdbTime = await bench("Lowdb find by id", async () => { |
| for (const id of lowdbIds) { |
| await lowDb.read(); |
| lowDb.data.providerConnections.find((c) => c.id === id); |
| } |
| }); |
|
|
| const speedup = (lowdbTime / sqliteTime).toFixed(2); |
| console.log(` β SQLite is ${speedup}x faster`); |
| }, 60000); |
|
|
| it(`saveRequestUsage ${N_ITEMS} entries`, async () => { |
| console.log(`\n[saveRequestUsage ${N_ITEMS}]`); |
|
|
| const sqliteTime = await bench("SQLite saveRequestUsage", async () => { |
| for (let i = 0; i < N_ITEMS; i++) { |
| await sqliteDb.saveRequestUsage({ |
| provider: "openai", model: `m-${i % 10}`, connectionId: `c-${i % 5}`, |
| tokens: { prompt_tokens: 100 + i, completion_tokens: 50 + i }, |
| endpoint: "/v1/chat/completions", status: "ok", |
| }); |
| } |
| }); |
|
|
| const lowdbTime = await bench("Lowdb push history + write", async () => { |
| lowDb.data.usageHistory = []; |
| for (let i = 0; i < N_ITEMS; i++) { |
| lowDb.data.usageHistory.push({ |
| timestamp: new Date().toISOString(), provider: "openai", model: `m-${i % 10}`, |
| connectionId: `c-${i % 5}`, tokens: { prompt_tokens: 100 + i, completion_tokens: 50 + i }, |
| endpoint: "/v1/chat/completions", status: "ok", cost: 0, |
| }); |
| await lowDb.write(); |
| } |
| }); |
|
|
| const speedup = (lowdbTime / sqliteTime).toFixed(2); |
| console.log(` β SQLite is ${speedup}x faster`); |
| }, 120000); |
|
|
| it(`getUsageStats(24h) repeat 50x`, async () => { |
| console.log(`\n[getUsageStats(24h) x 50]`); |
|
|
| const sqliteTime = await bench("SQLite getUsageStats(24h)", async () => { |
| for (let i = 0; i < 50; i++) await sqliteDb.getUsageStats("24h"); |
| }); |
|
|
| const lowdbTime = await bench("Lowdb read + aggregate", async () => { |
| for (let i = 0; i < 50; i++) { |
| await lowDb.read(); |
| const cutoff = Date.now() - 86400000; |
| const hist = lowDb.data.usageHistory.filter((h) => new Date(h.timestamp).getTime() >= cutoff); |
| const stats = { byProvider: {}, byModel: {} }; |
| for (const e of hist) { |
| if (!stats.byProvider[e.provider]) stats.byProvider[e.provider] = { requests: 0 }; |
| stats.byProvider[e.provider].requests++; |
| } |
| } |
| }); |
|
|
| const speedup = (lowdbTime / sqliteTime).toFixed(2); |
| console.log(` β SQLite is ${speedup}x faster`); |
| }, 60000); |
| }); |
|
|