Expired cache entries do not get flushed
Environment
Nitropack 2.8.1 Nuxt 3.10.1
Reproduction
Reproduction repo: https://stackblitz.com/edit/github-g1zu3w
- Start the app
- Click on "Go to A"
- See that "A" renders
- go back to the index route
- Click "Go to B"
- See that "B" renders
- Go in the file explorer to "/.nuxt/cache/nitro/routes/_" and see there are 3 records (index, A and B)
- Refresh "B" after more than 5 seconds
- See that the cache-record of "B" is updated, but the cache-record of "A" is still present (the fact that it stays present is the problem)
Describe the bug
When using SSR with cache old expired records never get flushed. This results in the filesystem or memory (depending on what cache-storage is used) to fill up over time.
Note that every unique URL is a cache-record, this includes query-parameters. So users can potentially fill up the cache of any Nuxt/Nitro server with ease, resulting in down time.
Additional context
No response
Logs
No response
Small addition as I've been working on this to solve the out of memory issue. The default storage driver configured for cache storage at build time is a memory driver. So each time a page is cached an item is pushed to the memory cache. This item will exist forever, because the driver has no TTL option.
See: https://github.com/unjs/nitro/blob/5b0e150e1fcdaba8ac6f75dfe40c28b42b5a188c/src/runtime/cache.ts#L121
As temporary solution we configured the storage driver with redis at runtime using a plugin.
I think this is partially fixed in current v2 branch also in v3, but that would only work for drivers that do support TTL like redis and not for a filesystem or memory drivers.
I want to raise another thing here as well, for use-cases like tracing, it means cache hits would be incorrectly reported for such drivers.
I'm thinking a good compromise here is to require the storage API connectors/drivers to always support TTL, and move the TTL validation logic to the driver/connector implementation to be decided there rather than the framework.
For example:
- For storage that supports TTL (e.g: redis): Already passing TTL to the underlying connector, no issue.
-
For storage that doesn't support TTL (e.g: filesystem): Make it so that
getoperations perform the same TTL check that Nitro uses, so if an item expires, flush it and returnnull.
This would make all get operations consistent across all drivers when it comes to TTL and expiry. It doesn't solve flushing for the original issue here.