Reapply "Add valkey cache handler to next app (#1210)" (#1215)#1220
Conversation
tefkah
left a comment
There was a problem hiding this comment.
if it would be possible to use a fallback i think that would be ideal to decrease possible downtime/manual fiddling! ideal behavior to me in case it can't reach valkey
- fallback to file system caching for now (or no caching, that's also fine)
- try to reconnect in the meantime
- if connection established, start using valkey cache
tefkah
left a comment
There was a problem hiding this comment.
whoops, i meant to request changes! if the behavior described above turns out not to be possible, i think at least creating a sentry issue would be nice (maybe it already does that if you throw)
| redisClient = new Redis({ | ||
| host: process.env.VALKEY_HOST, | ||
| lazyConnect: true, | ||
| commandTimeout: 1000, | ||
| retryStrategy: (times) => { | ||
| console.log("Retrying redis connection attempt:", times); | ||
| return (2 ^ times) + Math.random() * 1000; | ||
| }, | ||
| }); | ||
|
|
||
| await redisClient.connect(); |
There was a problem hiding this comment.
I'd love to reuse the singleton client from lib/redis but I can't seem to import it in this file (lib/redis.ts gets inlined/chunked somewhere, and the build process probably doesn't look at imports in this file). So we have to repeat some of the connection logic + params, and we have two clients per instance, but at least it's not more!
| /** | ||
| * Solution taken from here: https://github.com/vercel/next.js/discussions/48324#discussioncomment-10542097 | ||
| * Creates a redis handler based on fortedigital's redis-strings handler, but using the ioredis | ||
| * client |
There was a problem hiding this comment.
the node-redis client does not handle reconnecting well at all and it seems like many people have reported this issue for many years, with no fix. so I switched to ioredis, which both handles reconnection properly and has much clearer documentation. unfortunately @nescha/cache-handler and the @fortedigital handlers are tightly coupled to node-redis, so I've inlined a compatible version of the redis-strings handler here. the main changes were dropping the client.isReady check (which doesn't do anything), removing the per command timeouts (because that's set on the client), and replacing the method calls with lowercase versions to match the ioredis api. see 4346195 for the exact diff from the fortedigital handler
| COPY --from=withpackage --chown=node:node /usr/src/app/core/.env.docker ./core/.env | ||
|
|
||
| CMD node core/server.js | ||
| CMD ["node", "core/server.js"] |
There was a problem hiding this comment.
Apparently using non-json arguments here can prevent the server from responding to signals sent to the container
|
This works much better after switching to ioredis. The client will attempt to reconnect automatically, and it will simply fall back to uncached behavior when the cache is unreachable. Unfortunately I couldn't get Terraform plan for env var change, health check, and restart policy |
|
Changes applied after i lowered the health check timeout because of this error: |
Issue(s) Resolved
Resolves #1131
High-level Explanation of PR
This PR changes core to use valkey as its cache, as opposed to the filesystem. Theoretically it should be automatically instrumented by sentry since we're using a supported redis client.
I haven't yet set a max size, because I'm really not sure what it should be. IMO we shouldn't really worry about that yet, since the large values are a problem for the node server which can't gracefully run out of memory (which we should fix), whereas valkey will simply evict keys. Once we have some data about how the cache is performing, we can tune that configuration more.
Test Plan
redis-cli monitor. Send a few requests and verify that you see valkey being used. Stop valkey (pnpm -w dev:cache:stop). Confirm the app still serves requests.