Skip to Content

www no longer serves stale base data until it is redeployed

Shipped 2026-08-27

www reads its base data — faqs, skills, news, videos, materials, tunnels — from app.locals.redisData. That object was filled once at boot and nothing ever invalidated it, so a www process served whatever it read at startup for its entire lifetime.

That mattered more than it sounds. Every CMS area — tunnels, news, skills, reference-materials, notifications — rebuilds the Redis keys on save. The rebuild was working correctly and reaching Redis. It just never reached www, which went on serving its snapshot until the next deploy, roughly weekly. An editor saving a news item saw the site unchanged with nothing to explain why.

getStoredData now re-reads a key once its copy is older than BASE_DATA_TTL_MS — an hour in production — using stale-while-revalidate: the cached copy is returned immediately and the refresh happens behind the request. Awaiting Redis on the request path would have swapped a silent staleness bug for a latency one, and these keys are read on nearly every public page.

Two details worth keeping in mind:

  • A failed refresh still stamps the timestamp. Without that, a Redis outage would make every request re-attempt the read — turning one broken dependency into a retry storm. It backs off for a full interval instead, and the last good copy keeps being served.
  • The existing recovery path is unchanged. A genuinely missing key still triggers the immediate background re-read that getStoredData already did, so a www instance that boots against a cold Redis recovers on the next request rather than staying empty until a restart.

Found while updating the FAQs

TUN-850 rewrote 69 FAQ rows across English, French and Spanish. MySQL was committed and the API rebuilt the Redis keys correctly, but the live French page kept rendering the old content — voleur still appearing ten times — until iba-www was redeployed by hand.

The FAQ runbook and the reasons it is easy to get wrong are now documented on the Redis infrastructure page: commit before touching anything else, delete the keys before restarting the API because loadRedisBaseData() skips ones that already exist, and use JSON.GET rather than GET when verifying, since these are RedisJSON values and a plain GET returns nil on them.

Last updated on