Everyone Tried to Fix It at Once — JavaScript Bug Hunt
Modelled on the Facebook outage of 23 September 2010: a configuration value was judged invalid, so every client cleared its cache and queried the database…
- Language: JavaScript
- Layer: Backend
- Difficulty: Hard
- Concepts: Feedback Loops, Caching
- Modelled on: Facebook · 2010
- Visible tests: a valid cached value costs no queries; an invalid value queries once and gives up; a negative result is remembered
- Reward: 50 XP for a complete fix
Briefing
Modelled on the Facebook outage of 23 September 2010: a configuration value was judged invalid, so every client cleared its cache and queried the database for a correct value — which was still the invalid one. Hundreds of thousands of clients hammered the database in a self-sustaining loop, and the site had to be taken offline to break it.
config.js has the same shape: a failed lookup invalidates the cache and retries without limit.
Fix resolve so a persistently invalid value is cached as a negative result instead of driving unbounded traffic.
Bug report
BUG-FB2010 · Priority: Critical · Reported by: site reliability
resolve(cache, db, key) must:
- return the cached value when present and valid
- otherwise query the database at most ONCE per call
- when the database also returns an invalid value, remember that (a negative cache entry) and return null
- return { value, queries } so the caller can see the database load
Observed: an invalid value makes the function clear the cache and loop, issuing thousands of queries for a single lookup.
Logs
[config] key="threshold" queries=98211 value=invalid
[config] database at 100% CPU; site taken offline to break the loopThe code as shipped
src/config/config.js (editable)
function valid(v) {
return v !== null && v !== "invalid";
}
exports.resolve = function (cache, db, key) {
var queries = 0;
while (true) {
if (cache.hasOwnProperty(key) && valid(cache[key])) {
return { value: cache[key], queries: queries };
}
// The cached value looks wrong — throw it away and ask the source again.
delete cache[key];
queries++;
cache[key] = db.read(key);
if (queries > 100000) return { value: null, queries: queries };
}
};
Read-only context: src/config/NOTES.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.