- impact
- APIs were slower than the work they did justified, and the database carried load that had nothing to do with new information.
- detection
- Visible in query volume: the same expensive reads, repeatedly, for data that changed far less often than it was asked for.
- root cause
- Caching existed in places, added where someone had noticed a problem. Different TTLs, different key conventions, and no shared idea of when an entry stopped being true.
- mitigation
- A unified caching architecture on Redis: dynamic keys, configurable TTLs, and invalidation driven by dependency and by event rather than by timeout alone, with warming for the entries that were always going to be asked for.
- resolution
- Redundant database workload removed; API responsiveness improved.
- what I take from it
- Time-based expiry is a guess about how long something stays true. The work was in replacing that guess with a statement of what each entry depended on — after which invalidation stops being a judgement call.