From 175c770171157a09b9d0337b8d4a4cab88fea352 Mon Sep 17 00:00:00 2001 From: Josh Lehman Date: Mon, 2 Mar 2026 13:44:37 -0800 Subject: [PATCH] fix: address session-store cache review feedback --- src/config/cache-utils.ts | 19 ++++++++++--------- src/config/sessions.cache.test.ts | 4 ++-- src/config/sessions/store.ts | 29 ++++++++++++++--------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/config/cache-utils.ts b/src/config/cache-utils.ts index 68c07003752..e0024c0983f 100644 --- a/src/config/cache-utils.ts +++ b/src/config/cache-utils.ts @@ -18,17 +18,18 @@ export function isCacheEnabled(ttlMs: number): boolean { return ttlMs > 0; } -export function getFileMtimeMs(filePath: string): number | undefined { - try { - return fs.statSync(filePath).mtimeMs; - } catch { - return undefined; - } -} +export type FileStatSnapshot = { + mtimeMs: number; + sizeBytes: number; +}; -export function getFileSizeBytes(filePath: string): number | undefined { +export function getFileStatSnapshot(filePath: string): FileStatSnapshot | undefined { try { - return fs.statSync(filePath).size; + const stats = fs.statSync(filePath); + return { + mtimeMs: stats.mtimeMs, + sizeBytes: stats.size, + }; } catch { return undefined; } diff --git a/src/config/sessions.cache.test.ts b/src/config/sessions.cache.test.ts index 48f306adb4d..7001b45c011 100644 --- a/src/config/sessions.cache.test.ts +++ b/src/config/sessions.cache.test.ts @@ -220,12 +220,12 @@ describe("Session Store Cache", () => { "session:1": createSessionEntry({ sessionId: "id-1", displayName: "Original" }), "session:2": createSessionEntry({ sessionId: "id-2", displayName: "Added" }), }; + const preWriteStat = fs.statSync(storePath); const json2 = JSON.stringify(store2, null, 2); fs.writeFileSync(storePath, json2); // Force mtime to match the cached value so only size differs - const stat = fs.statSync(storePath); - fs.utimesSync(storePath, stat.atime, stat.mtime); + fs.utimesSync(storePath, preWriteStat.atime, preWriteStat.mtime); // The cache should detect the size change and reload from disk const loaded2 = loadSessionStore(storePath); diff --git a/src/config/sessions/store.ts b/src/config/sessions/store.ts index 7ade8462cd6..0790bed566c 100644 --- a/src/config/sessions/store.ts +++ b/src/config/sessions/store.ts @@ -17,12 +17,7 @@ import { normalizeSessionDeliveryFields, type DeliveryContext, } from "../../utils/delivery-context.js"; -import { - getFileMtimeMs, - getFileSizeBytes, - isCacheEnabled, - resolveCacheTtlMs, -} from "../cache-utils.js"; +import { getFileStatSnapshot, isCacheEnabled, resolveCacheTtlMs } from "../cache-utils.js"; import { loadConfig } from "../config.js"; import type { SessionMaintenanceConfig, SessionMaintenanceMode } from "../types.base.js"; import { enforceSessionDiskBudget, type SessionDiskBudgetSweepResult } from "./disk-budget.js"; @@ -213,9 +208,11 @@ export function loadSessionStore( if (!opts.skipCache && isSessionStoreCacheEnabled()) { const cached = SESSION_STORE_CACHE.get(storePath); if (cached && isSessionStoreCacheValid(cached)) { - const currentMtimeMs = getFileMtimeMs(storePath); - const currentSizeBytes = getFileSizeBytes(storePath); - if (currentMtimeMs === cached.mtimeMs && currentSizeBytes === cached.sizeBytes) { + const currentFileStat = getFileStatSnapshot(storePath); + if ( + currentFileStat?.mtimeMs === cached.mtimeMs && + currentFileStat?.sizeBytes === cached.sizeBytes + ) { // Return a deep copy to prevent external mutations affecting cache return structuredClone(cached.store); } @@ -230,7 +227,8 @@ export function loadSessionStore( // A short synchronous backoff (50 ms via `Atomics.wait`) is enough for the // writer to finish. let store: Record = {}; - let mtimeMs = getFileMtimeMs(storePath); + let fileStat = getFileStatSnapshot(storePath); + let mtimeMs = fileStat?.mtimeMs; let serializedFromDisk: string | undefined; const maxReadAttempts = process.platform === "win32" ? 3 : 1; const retryBuf = maxReadAttempts > 1 ? new Int32Array(new SharedArrayBuffer(4)) : undefined; @@ -247,7 +245,8 @@ export function loadSessionStore( store = parsed; serializedFromDisk = raw; } - mtimeMs = getFileMtimeMs(storePath) ?? mtimeMs; + fileStat = getFileStatSnapshot(storePath) ?? fileStat; + mtimeMs = fileStat?.mtimeMs; break; } catch { // File missing, locked, or transiently corrupt — retry on Windows. @@ -295,7 +294,7 @@ export function loadSessionStore( loadedAt: Date.now(), storePath, mtimeMs, - sizeBytes: getFileSizeBytes(storePath), + sizeBytes: fileStat?.sizeBytes, serialized: serializedFromDisk, }); } @@ -664,7 +663,7 @@ function updateSessionStoreWriteCaches(params: { store: Record; serialized: string; }): void { - const mtimeMs = getFileMtimeMs(params.storePath); + const fileStat = getFileStatSnapshot(params.storePath); SESSION_STORE_SERIALIZED_CACHE.set(params.storePath, params.serialized); if (!isSessionStoreCacheEnabled()) { SESSION_STORE_CACHE.delete(params.storePath); @@ -674,8 +673,8 @@ function updateSessionStoreWriteCaches(params: { store: structuredClone(params.store), loadedAt: Date.now(), storePath: params.storePath, - mtimeMs, - sizeBytes: getFileSizeBytes(params.storePath), + mtimeMs: fileStat?.mtimeMs, + sizeBytes: fileStat?.sizeBytes, serialized: params.serialized, }); }