← Two Hard Things: caching bugs

Two Hard Things: caching bugs · Part 6

Your cache just showed someone else's account

· by Tony Tang

Cache /dashboard by URL and the first user's page is served to everyone. On Christmas Day 2015, a caching config change at Steam may have shown about 34,000 users' personal pages to other people. Fix: the cache key must contain everything the response depends on (user, tenant, locale, role), and personal HTTP responses get Cache-Control: private.

Comment below: What's in your cache keys?

Two Hard Things: a series on the caching bugs you already ship, and how to fix them. "There are only two hard things in computer science: cache invalidation and naming things." (Phil Karlton) All code is real TypeScript on Node, typechecked before it went on screen, and every claim was reproduced against real Redis and Postgres.

Transcript

Your cache just showed a user someone else's account. You cache the dashboard by URL: /dashboard. Same URL for every user. The first user to load it fills the cache with their name, their orders, their address. Everyone after them gets that page. Fast, too. This happened for real. On Christmas Day 2015, a caching config change at Steam may have shown about 34,000 users' personal pages to other people. Fix. The cache key must contain everything the response depends on. User ID, tenant, locale, role. Build the key from the inputs the function actually reads, not from the URL. And over HTTP, a personal response gets Cache-Control: private, so no shared cache ever stores it. Best practice. If the response can differ, the key must differ. Speaking of Cache-Control, no-cache doesn't mean what you think. Part seven. What's in your cache keys?

More from this series