The invented object
Everything below is invented for illustration. It is not a measurement of any real system and not a customer delivery. A product record is cached under a key built from the product id. The agreed policy is: delete the key after the database commit, let the next read refill it, and expire every entry after 60 seconds. The 60 seconds is the longest an old value may stay visible, because a reader that began before the commit can store the old value after the delete, and then it stays until its entry expires.
If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.
key shape (in words): "product" + ":" + product id
expiry: 60 seconds, set on every refill
policy: delete the key after the database commit
agreed window: 60 seconds, the longest an old value may stay visible
usual case: the next read after the delete returns the new value
worst case: a reader that began before the commit stores the old value
after the delete; it is gone when its entry expires, at most 60 secondsThe write paths
Every way the product can change gets a row. A path missing from this table is the usual cause of a stale value.
If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.
path before after (agreed policy)
admin edit form updates DB, deletes key deletes key AFTER commit
bulk price import updates DB only deletes each changed key after commit
nightly stock sync job updates DB only deletes each changed key after commit
support tool "fix title" updates DB, SET key (no delete key after commit;
expiry) -> lives for ever no direct SET
product deleted leaves key deletes key after commitThe tests each path gets
Each row has a test, and the tests must fail on the original code.
- Update, then read: once the delete has run, the next read returns the new value.
- Paused reader: a reader is paused after its database read and before its cache write, an update commits and deletes the key, and the reader is released. Its write may put the old value back, so the test reads straight away and again after 60 seconds, and the old value must be gone by then.
- Failed delete: the delete step is made to fail after the commit, and the old value must be gone after 60 seconds.
- Overwrite: after a refill the key has the agreed expiry, not none.
- Missing path: a code search for every place that saves a product finds no path outside the table.
Limits, and the priced enquiry
The table is invented. No cache design removes the brief window around an update; the policy states how long it is, here 60 seconds, and a test enforces it. If 60 seconds is too long, a versioned key closes it further: the writer increments a version after the commit, readers put the version in the key, and a late refill lands in an entry nobody reads. The fixed-scope stale-cache job produces a table and tests like this for one real object type on a staging Redis, at £245 as an untested proposal, paid after you sign off. It does not tune Redis or clear production keys.
Sources and limits
- Redis: SET command Checked 2026-10-11.
- SET discards any earlier time to live unless KEEPTTL (since 6.0.0) is given.
- Redis: EXPIRE command Checked 2026-10-11.
- Commands that overwrite a key clear its timeout; commands that alter the value in place leave it.