File size: 6,370 Bytes
88c4c60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | // Benchmark: SQLite vs lowdb on equivalent workloads.
// Run: cd app/tests && npm test -- db-benchmark
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) {
// warmup
await fn();
const t0 = performance.now();
await fn();
const dt = performance.now() - t0;
console.log(` ${label.padEnd(40)} ${fmt(dt)}`);
return dt;
}
beforeAll(async () => {
// SQLite setup
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();
// Lowdb setup β direct lowdb usage (mimics legacy behavior)
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);
});
|