From febe1b2420c850aa17c12539a5a479c0438506f8 Mon Sep 17 00:00:00 2001 From: Tristan Knight Date: Sun, 11 Oct 2026 02:45:37 +0100 Subject: [PATCH] perf(usage): usage scans skip OpenCode legacy messages older than the window (#17254) --- .../src/server/usage.test.ts | 45 +++++++++++++++++++ .../provider-opencode/src/server/usage.ts | 22 +++++---- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/packages/provider-opencode/src/server/usage.test.ts b/packages/provider-opencode/src/server/usage.test.ts index 7575c3659040ef8bbdeda78e8cf6c9b943dd8f16..b11abcd49720583fa9eda0d00b6b74e81e85fa2f 100644 --- a/packages/provider-opencode/src/server/usage.test.ts +++ b/packages/provider-opencode/src/server/usage.test.ts @@ -137,5 +137,50 @@ describe("readOpenCodeUsage", () => { assert.isAbove(Number((yield* fileSystem.stat(path.join(dir, "opencode.db-wal"))).size), 0); }).pipe(Effect.provide(NodeServices.layer)), ); + + it.effect("reads only legacy OpenCode messages written since the window opened", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const dir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "usage-reader-test-" }); + const sinceMs = 1780000000000; + const legacy = path.join(dir, "storage", "message", "session-1"); + yield* fileSystem.makeDirectory(legacy, { recursive: true }); + const write = Effect.fn(function* (id: string, mtimeMs: number) { + const file = path.join(legacy, `${id}.json`); + yield* fileSystem.writeFileString( + file, + JSON.stringify({ + id, + sessionID: "session-1", + role: "assistant", + modelID: "claude-sonnet-4-5", + // In range, so an old file that was read would produce a record. + time: { created: sinceMs + 1000 }, + tokens: { input: 100, output: 20 }, + }), + ); + yield* fileSystem.utimes(file, mtimeMs / 1000, mtimeMs / 1000); + return file; + }); + yield* write("msg-old", sinceMs - 60_000); + + const stale = yield* readOpenCodeUsage(dir, sinceMs); + assert.deepStrictEqual(stale, { files: [], missing: false, error: false }); + + const recent = yield* write("msg-new", sinceMs + 60_000); + const result = yield* readOpenCodeUsage(dir, sinceMs); + assert.isFalse(result.missing); + assert.isFalse(result.error); + assert.deepStrictEqual( + result.files.map((file) => file.path), + [recent], + ); + assert.deepStrictEqual( + result.files.flatMap((file) => file.records).map((record) => record.dedupeKey), + ["opencode:msg-new"], + ); + }).pipe(Effect.provide(NodeServices.layer)), + ); }); import * as NodeSqlite from "node:sqlite"; diff --git a/packages/provider-opencode/src/server/usage.ts b/packages/provider-opencode/src/server/usage.ts index 677d502a72b0a7303bd81e4f2648f5bfff43dbda..07700109c6860558e4479488b256f706bb456350 100644 --- a/packages/provider-opencode/src/server/usage.ts +++ b/packages/provider-opencode/src/server/usage.ts @@ -16,6 +16,7 @@ import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as FileSystem from "effect/FileSystem"; +import * as Option from "effect/Option"; import * as Path from "effect/Path"; import type * as PlatformError from "effect/PlatformError"; import * as SqlClient from "effect/sql/SqlClient"; @@ -93,12 +94,12 @@ export interface OpenCodeUsageReadResult { const isNotFound = (cause: PlatformError.PlatformError) => cause.reason._tag === "NotFound"; const MESSAGE_PAGE_SIZE = 64; -/** A regular file or directory, never a symlink to one. */ -const entryType = Effect.fn("entryType")(function* (path: string) { +/** Stat of a regular file or directory; a symlink reports as itself and is never followed. */ +const entryInfo = Effect.fn("entryInfo")(function* (path: string) { const fileSystem = yield* FileSystem.FileSystem; const link = yield* Effect.exit(fileSystem.readLink(path)); - if (Exit.isSuccess(link)) return "SymbolicLink" as const; - return (yield* fileSystem.stat(path)).type; + if (Exit.isSuccess(link)) return { type: "SymbolicLink" as const, mtime: Option.none() }; + return yield* fileSystem.stat(path); }); /** Reads current SQLite and pre-migration JSON stores without modifying either. */ @@ -127,8 +128,8 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* ( names.filter((name) => /^opencode(?:-[a-zA-Z0-9_-]+)?\.db$/.test(name)), // One database removed between listing and stat must not hide the rest. (name) => - entryType(path.join(root, name)).pipe( - Effect.map((type) => type === "File"), + entryInfo(path.join(root, name)).pipe( + Effect.map((info) => info.type === "File"), Effect.catchTags({ PlatformError: (cause) => isNotFound(cause) ? Effect.succeed(false) : Effect.fail(cause), @@ -235,18 +236,21 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* ( yield* Effect.gen(function* () { for (const name of yield* fileSystem.readDirectory(directory)) { const entry = path.join(directory, name); - const type = yield* entryType(entry).pipe( + const info = yield* entryInfo(entry).pipe( Effect.catchTags({ PlatformError: (cause) => isNotFound(cause) ? Effect.succeed(null) : Effect.fail(cause), }), ); - if (type === "Directory") { + if (info?.type === "Directory") { directories.push(entry); - } else if (type === "File" && name.endsWith(".json")) { + } else if (info?.type === "File" && name.endsWith(".json")) { found = true; const id = name.slice(0, -5); if (seen.has(`opencode:${id}`)) continue; + // A message cannot be created after its file was last written, so a + // file untouched since the window opened holds nothing in range. + if (Option.exists(info.mtime, (mtime) => mtime.getTime() < sinceMs)) continue; const file = { path: entry, records: [] as UsageRecord[] }; files.push(file); yield* fileSystem.readFileString(entry).pipe( -- 2.54.0