Cache the misses too, on a shorter TTL
A lookup that finds nothing writes nothing to the cache, so every repeat of the same bad id hits the database. Scrapers and broken clients find those ids and keep asking for them. Write a sentinel on a miss as well, with a much shorter TTL than a hit gets.
const row = await db.user(id);
await redis.set(key, row ? JSON.stringify(row) : "__missing__", { EX: row ? 300 : 30 });Thirty seconds soaks up the flood and still lets a row created a moment later show up fast. Pick a sentinel no real value can produce, or a cached nothing looks just like a cache miss.
cachingperformance