nitro icon indicating copy to clipboard operation
nitro copied to clipboard

SWR prevent cache expiration

Open julbd opened this issue 9 months ago • 5 comments

Environment

Nitro 2.10.4

Reproduction

  routeRules: {
    '/': { swr: true, maxAge: 60, staleMaxAge: 60 },
  },

Describe the bug

The current implementation prevent cache clearing when swr is enabled.

https://github.com/nitrojs/nitro/blame/4a57c822d98701ef8f7a07aff7eb60be4c1888e2/src/runtime/internal/cache.ts#L124

This make the cached value being indefinitely reused and, in some case, massive cache accumulation. For heavy endpoint/functions, disabling swr may not be an option, as it can lead to extended unavailability.

The expected behavior would be to calculate and use the correct ttl using staleMaxAge.

Additional context

No response

Logs


julbd avatar Feb 20 '25 19:02 julbd

Related to https://github.com/nitrojs/nitro/pull/2783 (@pi0)

julbd avatar Feb 20 '25 19:02 julbd

Also, what is the expected configuration when "native invalidation" is not supported by the cache driver ? I am misundestranding the issue, does nitro/nuxt ignore the still-existing outdated cache ?

julbd avatar Feb 20 '25 20:02 julbd

Even if the storage layer has a proper eviction algorithm, not setting a TTL may lead to unnecessary memory usage, suboptimal cache management, and added monitoring complexity.

I'm currently facing an issue with increased cache costs due to this behavior with swr. Here are the changes made:

  • Before: maxAge = x, swr disabled
  • After: maxAge + staleMaxAge = x, swr enabled

The cache storage is Redis with LRU eviction configured.

Cache costs have risen to prevent excessive evictions. Additionally, observability has become more complex since we can no longer rely on the Redis hit ratio since some keys should have expired are still being hit.

emmanuelgautier avatar Mar 29 '25 16:03 emmanuelgautier

I was saw in a production load a memory leak dua a short (5sec) but exists SWR. staleMaxAge was used but not working on production from some reason (using standard memoryCache).

MosheL avatar May 28 '25 05:05 MosheL

for now, a ugly workaround for the SWR:

const storage = useStorage();

setInterval(async ()=>{
    const keys = (await storage.getKeys()).filter(f => f.startsWith("cache:nitro"));
      await Promise.all(keys.map(key => storage.removeItem(key)));
}, 20*60000);

place in a server module or server route.

MosheL avatar Jun 03 '25 15:06 MosheL