authorgravatar for admin@snappeh.comTristan Knight <admin@snappeh.com> 2026-10-11 02:45:37+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-10 18:45:37-07:00
logfebe1b2420c850aa17c12539a5a479c0438506f8
tree68a1fa226334ee046f5f6f5a7810825e60016e38
parent4258c8f14ddf6affe67c4e1d5d85ec834e6c4346
signature Signed by PGP key B5690EEEBB952194

perf(usage): usage scans skip OpenCode legacy messages older than the window (#17254)


2 files changed, 58 insertions(+), 9 deletions(-)

packages/provider-opencode/src/server/usage.test.ts+45
......@@ -137,5 +137,50 @@ describe("readOpenCodeUsage", () => {
137137 assert.isAbove(Number((yield* fileSystem.stat(path.join(dir, "opencode.db-wal"))).size), 0);
138138 }).pipe(Effect.provide(NodeServices.layer)),
139139 );
140
141 it.effect("reads only legacy OpenCode messages written since the window opened", () =>
142 Effect.gen(function* () {
143 const fileSystem = yield* FileSystem.FileSystem;
144 const path = yield* Path.Path;
145 const dir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "usage-reader-test-" });
146 const sinceMs = 1780000000000;
147 const legacy = path.join(dir, "storage", "message", "session-1");
148 yield* fileSystem.makeDirectory(legacy, { recursive: true });
149 const write = Effect.fn(function* (id: string, mtimeMs: number) {
150 const file = path.join(legacy, `${id}.json`);
151 yield* fileSystem.writeFileString(
152 file,
153 JSON.stringify({
154 id,
155 sessionID: "session-1",
156 role: "assistant",
157 modelID: "claude-sonnet-4-5",
158 // In range, so an old file that was read would produce a record.
159 time: { created: sinceMs + 1000 },
160 tokens: { input: 100, output: 20 },
161 }),
162 );
163 yield* fileSystem.utimes(file, mtimeMs / 1000, mtimeMs / 1000);
164 return file;
165 });
166 yield* write("msg-old", sinceMs - 60_000);
167
168 const stale = yield* readOpenCodeUsage(dir, sinceMs);
169 assert.deepStrictEqual(stale, { files: [], missing: false, error: false });
170
171 const recent = yield* write("msg-new", sinceMs + 60_000);
172 const result = yield* readOpenCodeUsage(dir, sinceMs);
173 assert.isFalse(result.missing);
174 assert.isFalse(result.error);
175 assert.deepStrictEqual(
176 result.files.map((file) => file.path),
177 [recent],
178 );
179 assert.deepStrictEqual(
180 result.files.flatMap((file) => file.records).map((record) => record.dedupeKey),
181 ["opencode:msg-new"],
182 );
183 }).pipe(Effect.provide(NodeServices.layer)),
184 );
140185});
141186import * as NodeSqlite from "node:sqlite";
packages/provider-opencode/src/server/usage.ts+13-9
......@@ -16,6 +16,7 @@ import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient";
1616import * as Effect from "effect/Effect";
1717import * as Exit from "effect/Exit";
1818import * as FileSystem from "effect/FileSystem";
19import * as Option from "effect/Option";
1920import * as Path from "effect/Path";
2021import type * as PlatformError from "effect/PlatformError";
2122import * as SqlClient from "effect/sql/SqlClient";
......@@ -93,12 +94,12 @@ export interface OpenCodeUsageReadResult {
9394const isNotFound = (cause: PlatformError.PlatformError) => cause.reason._tag === "NotFound";
9495const MESSAGE_PAGE_SIZE = 64;
9596
96/** A regular file or directory, never a symlink to one. */
97const entryType = Effect.fn("entryType")(function* (path: string) {
97/** Stat of a regular file or directory; a symlink reports as itself and is never followed. */
98const entryInfo = Effect.fn("entryInfo")(function* (path: string) {
9899 const fileSystem = yield* FileSystem.FileSystem;
99100 const link = yield* Effect.exit(fileSystem.readLink(path));
100 if (Exit.isSuccess(link)) return "SymbolicLink" as const;
101 return (yield* fileSystem.stat(path)).type;
101 if (Exit.isSuccess(link)) return { type: "SymbolicLink" as const, mtime: Option.none<Date>() };
102 return yield* fileSystem.stat(path);
102103});
103104
104105/** Reads current SQLite and pre-migration JSON stores without modifying either. */
......@@ -127,8 +128,8 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (
127128 names.filter((name) => /^opencode(?:-[a-zA-Z0-9_-]+)?\.db$/.test(name)),
128129 // One database removed between listing and stat must not hide the rest.
129130 (name) =>
130 entryType(path.join(root, name)).pipe(
131 Effect.map((type) => type === "File"),
131 entryInfo(path.join(root, name)).pipe(
132 Effect.map((info) => info.type === "File"),
132133 Effect.catchTags({
133134 PlatformError: (cause) =>
134135 isNotFound(cause) ? Effect.succeed(false) : Effect.fail(cause),
......@@ -235,18 +236,21 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (
235236 yield* Effect.gen(function* () {
236237 for (const name of yield* fileSystem.readDirectory(directory)) {
237238 const entry = path.join(directory, name);
238 const type = yield* entryType(entry).pipe(
239 const info = yield* entryInfo(entry).pipe(
239240 Effect.catchTags({
240241 PlatformError: (cause) =>
241242 isNotFound(cause) ? Effect.succeed(null) : Effect.fail(cause),
242243 }),
243244 );
244 if (type === "Directory") {
245 if (info?.type === "Directory") {
245246 directories.push(entry);
246 } else if (type === "File" && name.endsWith(".json")) {
247 } else if (info?.type === "File" && name.endsWith(".json")) {
247248 found = true;
248249 const id = name.slice(0, -5);
249250 if (seen.has(`opencode:${id}`)) continue;
251 // A message cannot be created after its file was last written, so a
252 // file untouched since the window opened holds nothing in range.
253 if (Option.exists(info.mtime, (mtime) => mtime.getTime() < sinceMs)) continue;
250254 const file = { path: entry, records: [] as UsageRecord[] };
251255 files.push(file);
252256 yield* fileSystem.readFileString(entry).pipe(