Synthetic Industry

Job redis-stale-cache-after-update-invalidation-fixed · revised 11 October 2026

Fix one cached object that shows stale data after it changes, with the invalidation tested

One cached object shows old data after it changes; its Redis write, delete and expiry order is corrected and tests prove the next read is fresh and the old value gone within a stated window.

You might be seeing

  • An edit saves successfully but the page shows the old value
  • The stale value disappears at some later time or after a manual flush
  • The problem is intermittent and worse under traffic

No passwords, keys, card details or admin invites needed to start.

What usually happened

A cached copy of one kind of object is not refreshed when the object changes. The usual causes are that the entry is deleted before the database transaction commits and a concurrent reader refills it with the old value, that one code path updates the database but never touches the cache, that overwriting a key discards its expiry or the expiry is far too long, or that the writer and the reader build the key differently. The stale value then lives until expiry or restart.

Who it’s for: A founder or engineering lead whose users report old prices, profiles or settings after an update, until someone clears the cache by hand.

Usually starts when: A value changes in the database but the site keeps showing the old one for minutes or hours, and a flush or restart fixes it.

The result: After any change to the named object through any agreed write path, the next read after the invalidation returns the new value, shown by tests that include a reader paused halfway through an update. A reader that began before the commit can still leave the old value in the cache for a short time; the policy note states that window as a number of seconds, agreed with you, and a deterministic test shows the old value is gone by the end of it. Expiry behaves as agreed after an overwrite.

Check whether this job fits

These questions check that the stale value is coming from Redis and from a bounded set of write paths.

Does the stale value disappear when that one cache key is deleted?
Can you list the places that change this object?
How long may an old value remain visible?

Answer the questions to see whether this job fits.

Nothing is sent anywhere until you choose to email us.

Send an enquiry about this outcome

Checks you can run yourself

  1. Compare one cached value with its source

    On a test copy, read the cached value and the database row for one object that is showing stale data. Do not change either.

    redis-cli TTL product:42

    Look for: Whether the key has an expiry (a positive number), none (minus one) or does not exist (minus two). A key with no expiry that never refreshes points at an overwrite dropping the expiry. Send the numbers, not the values.

What you get

  • A pull request with the corrected cache code and tests
  • A table of write paths and what each now does to the cache
  • A note on the chosen policy: delete after commit or versioned key, the expiry ceiling, the window in seconds in which an old value can still be seen and why, and what happens on a cache miss under load
  • A read-only check your engineer can run to compare a cached value with the database for an object

Included

  • One object type (for example a product, a profile or a price list) and its Redis cache keys in one application
  • Map every code path that changes the object and every place the cache is read or written
  • Correct the order of write, commit and invalidate, the key naming and the expiry handling, using one of two designs chosen with you: delete after commit with an expiry ceiling, or a versioned key that the writer moves on after commit
  • Tests for update-then-read, a reader paused between its database read and its cache write, and expiry after overwrite

Not included

  • Redis server tuning, eviction policy, clustering or persistence changes
  • Deleting keys by pattern in production
  • More than one object type
  • Other cache stores
  • A guarantee that no reader ever sees an old value for a moment around an update; the note states the window as a number and a test enforces it

How we know it’s done

Agreed with you before work starts. Each check produces evidence you keep.

  1. For every agreed write path, an update followed by a read, after the invalidation step has run, returns the new value on staging.

    Evidence: Test output per write path.

  2. In a deterministic interleaving test, a reader is paused after its database read and before its cache write, an update then commits and invalidates, and the reader is released. Under delete-after-commit the old value may now be cached, and it is not served once the agreed window has passed; under the versioned key it is never served after the version has moved on.

    Evidence: Test output for the paused reader showing what a read returns immediately after release and after the window, with the window in seconds from the policy note.

  3. With the invalidation step made to fail after the commit, the old value is no longer served by the end of the agreed window.

    Evidence: Test output showing the remaining lifetime of the entry and the read result after the window.

  4. Overwriting the cached value leaves the key with the expiry the agreed policy states.

    Evidence: Test output showing the remaining lifetime before and after an overwrite.

  5. For each cause found, the test written for it fails when run against the original code and passes on the changed code.

    Evidence: Test output on the original code and on the changed code.

Sign-off. You run the tests, read the write-path table and the policy note, then sign off in writing and merge. Payment follows sign-off.

If it fails. If a write path still returns a stale value in the agreed test, you do not pay for this fixed scope and keep the findings. If the stale value is held outside Redis, we explain what we found and stop.

When it fits, and when we stop

It fits when

  • The application uses Redis as a cache in front of a database, and its code is available to us after agreement
  • The object has a small number of write paths that can be listed
  • The application can run on staging with a Redis instance and synthetic data
  • You can say, as a number of seconds, the longest an old value may stay visible after an update; it becomes the expiry ceiling. "None" needs a different design

We stop and tell you if

  • The stale data comes from a content delivery network or browser cache, not Redis
  • The object is changed by an outside system that never tells the application
  • The application cannot run on staging with Redis
  • The required freshness is exact at every instant, which needs a different design

What could go wrong

The change is a pull request in your repository. Reverting the commit restores the earlier cache behaviour. Entries written by the new code expire or are deleted by the normal paths; we flush nothing in production.

Scroll the table sideways to read it all.

RiskHow we handle it
Deleting the entry before the commit lets a concurrent reader refill it with the old value.Invalidation moves after the commit, and a test pauses a reader between its database read and its cache write.
A reader that began before the commit stores the old value after the invalidation, so it is served until it expires.Every entry expires within the agreed ceiling, or the versioned key moves the next readers on to a new entry. The note states the window as a number and the paused-reader test enforces it.
The process stops between the database commit and the invalidation.The expiry ceiling still removes the old value; a test makes the invalidation step fail and shows the value gone by the end of the window.
A missed write path leaves one route stale.The write-path table is part of the deliverable and the reviewer compares it with a code search.
A burst of misses after an invalidation overloads the database.The note describes the miss behaviour for the object and, where needed, a short guard on refills; it does not promise to remove load spikes.

A second reviewer checks that invalidation happens after the database commit, that every write path is covered, that the stated window matches the expiry or version design, and that the tests would fail on the original code. Your engineer reviews and merges.

How we deliver

We arrange the work and independent review, then show you the result against the agreed checks. You keep authority over your systems.

  • Agree the object, the write paths, the allowed window in seconds and the staging setup in writing
  • List every write path and every cache read and write for the object, and reproduce the stale read on staging
  • Choose the policy with you: delete after commit with an expiry ceiling equal to the window, or a versioned key (a version counter in Redis that the writer increments after commit and the reader puts in the key, with every entry expiring)
  • Fix key construction so the writer and the reader agree, and the expiry survives an overwrite as agreed
  • Add tests: update then read; a reader paused between its database read and its cache write while an update commits and invalidates; and expiry after overwrite. Confirm that, for each cause found, its test fails on the original code
  • Independent review of the diff and test runs, then hand over the pull request and the write-path table

This is a one-off job, not emergency cover or a subscription. We confirm eligibility, the total price, a start window and a delivery date before you accept. Work starts only after agreed inputs, secure access and any permissions are in place. Hosting and database charges are excluded unless the written quote includes them. No charge or booking is created by an enquiry.

Need to keep it working?

Discuss a project covering several cached objects, or a monthly review of cache hit rates and evictions.

Ongoing work is separately scoped and quoted: no monitoring, response-time guarantee or automatic subscription is included in this job.

Explore an ongoing engineering lane, or mention the responsibility you need in your enquiry.

What you can check

This is a new service. We have not delivered this job for a client yet.

Other ways to get this done

  • The Redis documentation for SET says an ordinary SET discards any earlier expiry unless KEEPTTL is used, and EXPIRE says commands that only alter a value leave the expiry alone. redis.io
  • The Redis eviction page explains how memory limits and policies decide which keys disappear, which is a different cause of missing cache entries. redis.io

Questions

Why not flush the cache after every change?

It makes every reader miss at once and loads the database. We invalidate the one object that changed.

Can you guarantee no stale reads at all?

No cache can. A reader that started just before an update can store the old value just after it. The note states how long that can last as a number of seconds, agreed with you, and a test that pauses a reader at the worst moment shows the old value is gone by then. If you need none at all, a cache is the wrong tool for that object.

What is the versioned key for?

It lets the next readers move on to a fresh entry as soon as the writer has finished, so a late refill lands in an entry nobody reads. It costs one extra Redis lookup per read, and old entries still expire. We use it only if the window of the simpler design is too long for you.

We cache several kinds of object. Is that one job?

Each object type is its own scope, since each has its own write paths. Several can be quoted together.

Send an enquiry

Send us

  • The object type, how it is cached (key shape in words) and how long it is kept
  • Which actions change the object, and what a user sees afterwards
  • The Redis version and the client library, if known
  • Do not send credentials, connection details or cached values in the first enquiry

Later, once you agree

  • The cache and write-path code through an authorised company-controlled repository route
  • A staging Redis instance and database with synthetic data
  • The agreed freshness: the longest an old value may remain visible, in seconds
  • The person who reviews and merges the pull request

You own the database, the code and every credential. We work on a copy you prepare, such as a staging database restored from a backup with personal data removed or replaced, and we hand work back as a pull request or a script. We never ask for production passwords, and we do not connect to your production database. You apply any change to production yourself, after a restore point that you have tested.

A public HTTPS link only, without login details, query strings or fragments. No code or logs.

Sending emails your enquiry and contact address to our team through our mail provider (Resend). It is not kept in a website database. Do not send passwords, keys, recovery links, confidential code or customer records. Your contact email is unverified; nothing is ordered, charged or reserved. Privacy notice.

Email fallback: open your mail app

If website submission is unavailable, review and send the fallback email yourself. An email fallback is not a website receipt. Or write to hello@syntheticindustry.ai with “redis-stale-cache-after-update-invalidation-fixed” as the subject.