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", () => {...@@ -137,5 +137,50 @@ describe("readOpenCodeUsage", () => {
137 assert.isAbove(Number((yield* fileSystem.stat(path.join(dir, "opencode.db-wal"))).size), 0);137 assert.isAbove(Number((yield* fileSystem.stat(path.join(dir, "opencode.db-wal"))).size), 0);
138 }).pipe(Effect.provide(NodeServices.layer)),138 }).pipe(Effect.provide(NodeServices.layer)),
139 );139 );
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 );
140});185});
141import * as NodeSqlite from "node:sqlite";186import * 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";...@@ -16,6 +16,7 @@ import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient";
16import * as Effect from "effect/Effect";16import * as Effect from "effect/Effect";
17import * as Exit from "effect/Exit";17import * as Exit from "effect/Exit";
18import * as FileSystem from "effect/FileSystem";18import * as FileSystem from "effect/FileSystem";
19import * as Option from "effect/Option";
19import * as Path from "effect/Path";20import * as Path from "effect/Path";
20import type * as PlatformError from "effect/PlatformError";21import type * as PlatformError from "effect/PlatformError";
21import * as SqlClient from "effect/sql/SqlClient";22import * as SqlClient from "effect/sql/SqlClient";
...@@ -93,12 +94,12 @@ export interface OpenCodeUsageReadResult {...@@ -93,12 +94,12 @@ export interface OpenCodeUsageReadResult {
93const isNotFound = (cause: PlatformError.PlatformError) => cause.reason._tag === "NotFound";94const isNotFound = (cause: PlatformError.PlatformError) => cause.reason._tag === "NotFound";
94const MESSAGE_PAGE_SIZE = 64;95const MESSAGE_PAGE_SIZE = 64;
9596
96/** A regular file or directory, never a symlink to one. */97/** Stat of a regular file or directory; a symlink reports as itself and is never followed. */
97const entryType = Effect.fn("entryType")(function* (path: string) {98const entryInfo = Effect.fn("entryInfo")(function* (path: string) {
98 const fileSystem = yield* FileSystem.FileSystem;99 const fileSystem = yield* FileSystem.FileSystem;
99 const link = yield* Effect.exit(fileSystem.readLink(path));100 const link = yield* Effect.exit(fileSystem.readLink(path));
100 if (Exit.isSuccess(link)) return "SymbolicLink" as const;101 if (Exit.isSuccess(link)) return { type: "SymbolicLink" as const, mtime: Option.none<Date>() };
101 return (yield* fileSystem.stat(path)).type;102 return yield* fileSystem.stat(path);
102});103});
103104
104/** Reads current SQLite and pre-migration JSON stores without modifying either. */105/** Reads current SQLite and pre-migration JSON stores without modifying either. */
...@@ -127,8 +128,8 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (...@@ -127,8 +128,8 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (
127 names.filter((name) => /^opencode(?:-[a-zA-Z0-9_-]+)?\.db$/.test(name)),128 names.filter((name) => /^opencode(?:-[a-zA-Z0-9_-]+)?\.db$/.test(name)),
128 // One database removed between listing and stat must not hide the rest.129 // One database removed between listing and stat must not hide the rest.
129 (name) =>130 (name) =>
130 entryType(path.join(root, name)).pipe(131 entryInfo(path.join(root, name)).pipe(
131 Effect.map((type) => type === "File"),132 Effect.map((info) => info.type === "File"),
132 Effect.catchTags({133 Effect.catchTags({
133 PlatformError: (cause) =>134 PlatformError: (cause) =>
134 isNotFound(cause) ? Effect.succeed(false) : Effect.fail(cause),135 isNotFound(cause) ? Effect.succeed(false) : Effect.fail(cause),
...@@ -235,18 +236,21 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (...@@ -235,18 +236,21 @@ export const readOpenCodeUsage = Effect.fn("readOpenCodeUsage")(function* (
235 yield* Effect.gen(function* () {236 yield* Effect.gen(function* () {
236 for (const name of yield* fileSystem.readDirectory(directory)) {237 for (const name of yield* fileSystem.readDirectory(directory)) {
237 const entry = path.join(directory, name);238 const entry = path.join(directory, name);
238 const type = yield* entryType(entry).pipe(239 const info = yield* entryInfo(entry).pipe(
239 Effect.catchTags({240 Effect.catchTags({
240 PlatformError: (cause) =>241 PlatformError: (cause) =>
241 isNotFound(cause) ? Effect.succeed(null) : Effect.fail(cause),242 isNotFound(cause) ? Effect.succeed(null) : Effect.fail(cause),
242 }),243 }),
243 );244 );
244 if (type === "Directory") {245 if (info?.type === "Directory") {
245 directories.push(entry);246 directories.push(entry);
246 } else if (type === "File" && name.endsWith(".json")) {247 } else if (info?.type === "File" && name.endsWith(".json")) {
247 found = true;248 found = true;
248 const id = name.slice(0, -5);249 const id = name.slice(0, -5);
249 if (seen.has(`opencode:${id}`)) continue;250 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;
250 const file = { path: entry, records: [] as UsageRecord[] };254 const file = { path: entry, records: [] as UsageRecord[] };
251 files.push(file);255 files.push(file);
252 yield* fileSystem.readFileString(entry).pipe(256 yield* fileSystem.readFileString(entry).pipe(