fix(stats): added timeout to usage snapshot reads
Installed a five-second SQLite busy timeout before the read-only usage query and covered lock contention with a subprocess-backed regression test. Fixes #7300
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed provider usage window stats silently showing no data during SQLite contention by installing a five-second busy timeout on read-only agent database connections ([#7300](https://github.com/can1357/oh-my-pi/issues/7300)).
|
||||
|
||||
## [17.1.2] - 2026-07-24
|
||||
|
||||
### Added
|
||||
|
||||
@@ -59,6 +59,7 @@ export function readUsageSnapshots(sinceMs: number, dbPath = getAgentDbPath()):
|
||||
let db: Database | null = null;
|
||||
try {
|
||||
db = new Database(dbPath, { readonly: true });
|
||||
db.run("PRAGMA busy_timeout = 5000");
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT recorded_at, provider, account_key, email, account_id, limit_id, label, window_label, used_fraction, status
|
||||
|
||||
@@ -183,6 +183,35 @@ describe("readUsageSnapshots", () => {
|
||||
usedFraction: 0.3,
|
||||
});
|
||||
});
|
||||
|
||||
it("waits for a contended database instead of returning no snapshots", async () => {
|
||||
createAgentDb([snapshot({ recordedAt: T0, usedFraction: 0.3 })]);
|
||||
const locker = Bun.spawn(
|
||||
[
|
||||
process.execPath,
|
||||
"-e",
|
||||
`import { Database } from "bun:sqlite";
|
||||
const db = new Database(process.argv[1]);
|
||||
db.run("BEGIN EXCLUSIVE");
|
||||
process.stdout.write("locked\\n");
|
||||
// This integration probe needs a real SQLite lock lifetime; fake timers cannot advance a separate process.
|
||||
await Bun.sleep(100);
|
||||
db.run("COMMIT");
|
||||
db.close();`,
|
||||
getAgentDbPath(),
|
||||
],
|
||||
{ env: { HOME: process.env.HOME ?? "", PATH: process.env.PATH ?? "" }, stdout: "pipe", stderr: "pipe" },
|
||||
);
|
||||
const output = locker.stdout.getReader();
|
||||
const ready = await output.read();
|
||||
output.releaseLock();
|
||||
expect(new TextDecoder().decode(ready.value)).toContain("locked");
|
||||
|
||||
const rows = readUsageSnapshots(0);
|
||||
const [exitCode, stderr] = await Promise.all([locker.exited, new Response(locker.stderr).text()]);
|
||||
expect(exitCode, stderr).toBe(0);
|
||||
expect(rows).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getProviderDashboardStats", () => {
|
||||
|
||||
Reference in New Issue
Block a user