Address AI review findings:
- Bug: restore 30 s periodic eviction via setInterval so queries that
are never repeated don't accumulate forever (add setInterval/
clearInterval to ESLint globals to allow it)
- Bug: fix .finally() race – use identity check before deleting the
in-flight key so a waiting request's replacement promise is never
evicted by the original promise's cleanup handler
- Warning: replace `new URL(c.req.url).search` with a string-split
approach that cannot throw on relative URLs
- Warning: add MAX_CACHE_ENTRIES (500) cap with LRU-oldest eviction to
bound memory growth from callers with many unique variable sets
- Warning: prefix cache key with c.req.path so /graphql and / can
never produce cross-route cache collisions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Add server-side response cache + in-flight coalescing to Ponder's Hono
API layer (services/ponder/src/api/index.ts).
Previously every polling client generated an independent DB query, giving
O(users × 1/poll_interval) load. With a 5 s in-process cache keyed on the
raw request body (POST) or query string (GET), the effective DB hit rate
is capped at O(1/5s) regardless of how many clients are polling.
In-flight coalescing ensures that N concurrent identical queries that
arrive before the first response is ready all share a single DB hit
instead of each issuing their own. Expired entries are evicted every 30 s
to keep memory use bounded.
The 5 s TTL deliberately matches the existing Caddy `Cache-Control:
public, max-age=5` header so that if a caching proxy/CDN is layered in
front later, both layers stay in sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>