> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cache Invalidation TTLs

> Strategies for keeping caches consistent with databases, comparing TTL expiration and active invalidation plus mitigations for stampedes and race conditions

When you add a cache in front of your database, you get faster reads and reduced DB load — but you also introduce a consistency problem. The database is the canonical source of truth; the cache holds a copy. If those copies diverge, users can see stale data. For example, if Lionel Messi updates his bio in the database but the cache still serves the old bio, the system is correct at the DB level but wrong at the user-visible layer.

<Callout icon="lightbulb" color="#1CB2FE">
  Define how much staleness your application can tolerate before choosing an invalidation strategy. Measure the acceptable staleness window and use it to guide whether you need TTLs, active invalidation, or a hybrid approach.
</Callout>

There are two common strategies to keep cache and database consistent:

* TTL (time to live): let cached items expire automatically.
* Active invalidation: explicitly remove or update cache entries on database writes.

Below we explain both, when to use them, and common mitigations for their pitfalls.

## TTL (time to live)

TTL means writing items into the cache with an expiry time. Example: set a user bio in Redis with a 60-second TTL. When the TTL expires, Redis evicts the entry; the next request misses the cache, fetches the latest data from the database, and repopulates the cache.

TTL is a lazy approach: it trades eventual consistency for simplicity and scalability. It’s a good fit when short periods of staleness are acceptable (e.g., follower counts, trending metrics). But if your application cannot tolerate even a second of stale data (e.g., privacy settings), TTL alone is insufficient.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/ttl-cache-system-design-data-flow.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=38bf76538376bfffd8d427187ce567ab" alt="The image illustrates a system design using a Time to Live (TTL) cache mechanism, showing data flow between an app, Redis cache, and a database, with old and new bio information." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/ttl-cache-system-design-data-flow.jpg" />
</Frame>

### Cache stampede (thundering herd)

A common TTL-related failure mode is the cache stampede: when a very popular cache key expires, many clients request it simultaneously, causing a surge of identical DB queries. This can overload your database.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/cache-stampede-diagram-app-redis-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=d83886d3e353fa818850b2f31ae1aa4d" alt="The image is a diagram illustrating a cache stampede scenario in a system involving an app, Redis cache, and a database, depicting the flow of photo requests from a user." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/cache-stampede-diagram-app-redis-database.jpg" />
</Frame>

Mitigations for stampedes and TTL issues:

* Add jitter to TTLs so hot keys don’t all expire at the same instant.
* Request coalescing / single-flight: ensure only one in-flight DB fetch repopulates the cache for a given key.
* Per-key mutexes or locks to serialize refreshes.
* Background refresh (prefetching) for known hot keys.
* Serve a slightly stale value while refreshing asynchronously (stale-while-revalidate).

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/data-flow-ttl-redis-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=4695c8b8dc76ef7a48aa92c75416eeb5" alt="The image illustrates a data flow process using &#x22;TTL, Time to Live&#x22; with an app accessing data through Redis cache and a database, showing elements like follower count and bio update times." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/data-flow-ttl-redis-database.jpg" />
</Frame>

## Active invalidation

Active invalidation means the application updates or deletes the cached copy as part of the write flow. Typical pattern: write the new value to the DB, then delete or update the corresponding cache entry so future reads fetch fresh data. This approach minimizes the staleness window and is required when correctness is critical.

However, active invalidation can be tricky because of races. For example, a read may occur between the DB write and the cache delete, potentially repopulating the cache with the old value.

Common strategies to make active invalidation safer:

* Double-delete: delete the cache before and after writing to the DB (or delete after DB write and repeat shortly after).
* Write-through / write-behind: write the value to the cache as part of the DB update so the cache holds the new value immediately.
* Versioned keys or optimistic concurrency: include a version or timestamp in cache keys/values so readers can detect stale entries.
* Distributed locks or single-flight: serialize cache refreshes to avoid races.

<Callout icon="warning" color="#FF6B6B">
  If you invalidate the cache in the wrong order relative to the DB write, you risk serving stale entries. Design your write+invalidate ordering carefully and consider extra safeguards (double-delete, versioning, or locking) to guarantee correctness.
</Callout>

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/active-invalidation-bio-update-redis-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=86b8d5185b95f7507a36bfa98502b9ff" alt="The image illustrates a process of active invalidation involving an app, Redis cache, and a database, where a bio update is propagated through the system." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/active-invalidation-bio-update-redis-database.jpg" />
</Frame>

Active invalidation increases implementation complexity because every write path must include cache maintenance. It’s the right choice when staleness is unacceptable (security/permissions/privacy).

## Choosing the right approach

There’s no single answer — choose based on your correctness requirements, traffic patterns, and operational complexity:

* Use short TTLs (with jitter) for high-throughput, eventually-consistent data (home feed, counts).
* Use active invalidation for security- or privacy-sensitive fields (permissions, feature flags).
* Use longer TTLs for rarely-changing data (user profile information) to reduce read latency and DB load.
* Combine strategies: TTLs + jitter for general load reduction, request coalescing to avoid stampedes, and active invalidation for critical fields.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/ttl-trending-feed-privacy-settings-illustration.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=537537fc6a1f8fc74b99ae806dd4c721" alt="The image illustrates a choice between using a short TTL (Time to Live) for a trending feed, where a few seconds of staleness is acceptable, and ensuring privacy settings that can't be stale even for a second." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/ttl-trending-feed-privacy-settings-illustration.jpg" />
</Frame>

## Industry defaults (good starting points)

| Resource / Feature                            | Typical default                  | Rationale                                                                  |
| --------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------- |
| Home feed                                     | \~30-second TTL                  | Balances freshness with load reduction; short staleness acceptable         |
| Profile card                                  | Few minutes TTL                  | Profiles change infrequently; longer TTL reduces DB load                   |
| Trending list                                 | \~60-second TTL                  | Trends tolerate short delays; helps smooth traffic                         |
| Security-sensitive data (permissions/privacy) | No TTL — use active invalidation | Even momentary staleness can cause breaches; require immediate consistency |

These are tunable defaults — measure your latency, cache hit rate, and correctness requirements and iterate.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/industry-benchmarks-app-features-ttl.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=60841c4554cda3cc547bba0f231e96f8" alt="The image illustrates industry benchmarks for various app features like &#x22;Home Feed&#x22; and &#x22;Trending List&#x22; with their respective time-to-live (TTL) durations, alongside a phone interface showing privacy settings." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Cache-Invalidation-TTLs/industry-benchmarks-app-features-ttl.jpg" />
</Frame>

One final point: the worst bugs are not when the cache is empty. The worst bugs are when the cache confidently serves wrong data.

There’s an old joke that there are only two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/system-design-for-beginners/module/3ee9409c-c2ff-4102-9a76-af9840dc6e23/lesson/61093e0d-f42a-4ec2-833c-356964524728" />
</CardGroup>
