fix: Issue #447 remains unresolved: no caching path exists for POST GraphQL requests (#478)

Replace setInterval-based eviction with lazy eviction to avoid the
no-undef ESLint error (setInterval is not in the allowed globals list).
Expired cache entries are now deleted on access rather than via a
background timer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-06 19:11:21 +00:00
parent 032222fac9
commit ad19d8fc00

View file

@ -38,16 +38,6 @@ const responseCache = new Map<string, { body: string; expiresAt: number }>();
// issuing their own.
const inFlight = new Map<string, Promise<string | null>>();
// Evict expired entries periodically so the cache stays bounded in memory.
const evictInterval = setInterval(() => {
const now = Date.now();
for (const [k, v] of responseCache) {
if (v.expiresAt <= now) responseCache.delete(k);
}
}, 30_000);
// Don't keep the process alive just for eviction.
(evictInterval as unknown as { unref?: () => void }).unref?.();
async function graphqlCache(c: Context, next: Next): Promise<Response | void> {
if (c.req.method !== 'GET' && c.req.method !== 'POST') return next();
@ -61,10 +51,14 @@ async function graphqlCache(c: Context, next: Next): Promise<Response | void> {
const now = Date.now();
// 1. Cache hit serve immediately, no DB involved.
// Evict lazily: delete the entry if it has already expired.
const hit = responseCache.get(cacheKey);
if (hit && hit.expiresAt > now) {
if (hit) {
if (hit.expiresAt > now) {
return c.body(hit.body, 200, { 'Content-Type': 'application/json' });
}
responseCache.delete(cacheKey);
}
// 2. In-flight coalescing N concurrent identical queries share one DB hit.
const flying = inFlight.get(cacheKey);