mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-10-19 05:20:05 +00:00
Concurrent misses on the read caches (pricing, settings, connections, nodes, LKGP) now share one in-flight load, so the usage_history rollup no longer loads the full pricing table once per group after a pricing sync.
Maintainer follow-up on the branch: added a test proving a failed shared load rejects every waiter, clears the in-flight slot and is not cached (a mutation that keeps the rejected promise makes it fail).
Evidence (tip ed664fc8 merged into the head): 3/4 tests fail on the tip, 4/4 pass with the fix; db-read-cache, combo-cache-invalidation and model-catalog invalidation suites green; typecheck:core, check:open-sse-typecheck, eslint and check-file-size clean.
Thanks @cryptiklemur!
125 lines
4 KiB
TypeScript
125 lines
4 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-pricing-single-flight-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
|
|
const core = await import("../../src/lib/db/core.ts");
|
|
const readCache = await import("../../src/lib/db/readCache.ts");
|
|
const { rollupUsageHistoryBeforeDate } = await import("../../src/lib/usage/aggregateHistory.ts");
|
|
|
|
const PRICING_READ = "SELECT key, value FROM key_value WHERE namespace = ?";
|
|
|
|
function countPricingReads(db: ReturnType<typeof core.getDbInstance>) {
|
|
const original = db.prepare.bind(db);
|
|
const counter = { reads: 0 };
|
|
db.prepare = ((sql: string) => {
|
|
if (sql === PRICING_READ) counter.reads++;
|
|
return original(sql);
|
|
}) as typeof db.prepare;
|
|
return {
|
|
counter,
|
|
restore: () => {
|
|
db.prepare = original;
|
|
},
|
|
};
|
|
}
|
|
|
|
test.beforeEach(() => {
|
|
readCache.invalidateDbCache();
|
|
});
|
|
|
|
test.after(() => {
|
|
core.resetDbInstance();
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
});
|
|
|
|
test("concurrent getCachedPricing misses share one pricing load", async () => {
|
|
const db = core.getDbInstance();
|
|
const spy = countPricingReads(db);
|
|
try {
|
|
const results = await Promise.all(
|
|
Array.from({ length: 50 }, () => readCache.getCachedPricing())
|
|
);
|
|
assert.equal(spy.counter.reads, 3);
|
|
assert.ok(results.every((pricing) => pricing === results[0]));
|
|
} finally {
|
|
spy.restore();
|
|
}
|
|
});
|
|
|
|
test("invalidation during an in-flight pricing load starts a fresh load", async () => {
|
|
const db = core.getDbInstance();
|
|
const spy = countPricingReads(db);
|
|
try {
|
|
const before = readCache.getCachedPricing();
|
|
readCache.invalidateDbCache("pricing");
|
|
const after = readCache.getCachedPricing();
|
|
const [stale, fresh] = await Promise.all([before, after]);
|
|
assert.notEqual(stale, fresh);
|
|
assert.equal(spy.counter.reads, 6);
|
|
assert.equal(await readCache.getCachedPricing(), fresh);
|
|
assert.equal(spy.counter.reads, 6);
|
|
} finally {
|
|
spy.restore();
|
|
}
|
|
});
|
|
|
|
test("a failed shared pricing load rejects every waiter and is not cached", async () => {
|
|
const db = core.getDbInstance();
|
|
const original = db.prepare.bind(db);
|
|
let failures = 0;
|
|
db.prepare = ((sql: string) => {
|
|
if (sql === PRICING_READ && failures === 0) {
|
|
failures++;
|
|
throw new Error("pricing read failed");
|
|
}
|
|
return original(sql);
|
|
}) as typeof db.prepare;
|
|
try {
|
|
const waiters = Array.from({ length: 10 }, () => readCache.getCachedPricing());
|
|
const settled = await Promise.allSettled(waiters);
|
|
assert.equal(failures, 1);
|
|
assert.ok(settled.every((result) => result.status === "rejected"));
|
|
} finally {
|
|
db.prepare = original;
|
|
}
|
|
|
|
// The slot was cleared on error: the next call starts a fresh load and succeeds.
|
|
const spy = countPricingReads(db);
|
|
try {
|
|
const pricing = await readCache.getCachedPricing();
|
|
assert.equal(typeof pricing, "object");
|
|
assert.equal(spy.counter.reads, 3);
|
|
assert.equal(await readCache.getCachedPricing(), pricing);
|
|
assert.equal(spy.counter.reads, 3);
|
|
} finally {
|
|
spy.restore();
|
|
}
|
|
});
|
|
|
|
test("usage_history rollup reads pricing once for a day of distinct token shapes", async () => {
|
|
const db = core.getDbInstance();
|
|
const insert = db.prepare(
|
|
`INSERT INTO usage_history (provider, model, connection_id, tokens_input, tokens_output, success, latency_ms, timestamp)
|
|
VALUES (?, ?, ?, ?, ?, 1, 100, ?)`
|
|
);
|
|
db.transaction(() => {
|
|
for (let i = 0; i < 200; i++) {
|
|
insert.run("openai", "gpt-4o", "conn-1", 1000 + i, 50 + i, "2026-01-01T12:00:00.000Z");
|
|
}
|
|
})();
|
|
|
|
const spy = countPricingReads(db);
|
|
try {
|
|
const result = await rollupUsageHistoryBeforeDate("2026-01-02");
|
|
assert.equal(result.errors, 0);
|
|
assert.equal(result.processed, 200);
|
|
assert.equal(spy.counter.reads, 3);
|
|
} finally {
|
|
spy.restore();
|
|
}
|
|
});
|