# Invalidating Cache doesn't happen automatically

**URL:** <https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780>\
**Category:** Forms & APIs\
**Created:** [May 18, 2022, 9:51am UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780 "2022-05-18T09:51:18Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 18, 2022, 9:51am UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/1 "2022-05-18T09:51:18Z")

</div>

A cached page should be invalidated when something changes at the underlying resource. I probably don’t understand enough of the caching, so please help me out here.

Django implements “site-wide” caching and I used [this guide](https://docs.djangoproject.com/en/4.0/topics/cache/#the-per-site-cache-1) to setup my caching infrastructure (locally to test for now).

```
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
CACHE_TTL = 60 * 60 * 1
CACHE_MIDDLEWARE_SECONDS = CACHE_TTL

```

When I run my tests now, it works too well, as all tests fail, which change information in the database. Do I have to manually add a flag for cache invalidation? Should this even work (or are my settings wrong)? I saw [this question](https://stackoverflow.com/questions/61277009/django-rest-cache-invalidation), but in my case I don’t use any decorators, but the side wide caching. Nevertheless I tested using the `vary_on_headers('Authorization',)` which didn’t change the test results.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 18, 2022, 12:08pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/2 "2022-05-18T12:08:40Z")

</div>

I would disable all caches for testing.

From the docs:

> `FetchFromCacheMiddleware` caches GET and HEAD responses with status 200, where the request and response headers allow.

This means that subsequent requests to the same url are going to return the same response until the cache expires. Your view doesn’t even execute in those cases.

You could probably change your tests to ensure that they don’t retrieve cached versions, but I’m not sure it’s worth the effort in that direction.

(See the source code for the middleware in `django.middleware.cache` for more detailed information.)

---

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 18, 2022, 12:13pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/3 "2022-05-18T12:13:11Z")

</div>

Okay I see the point, but wouldn’t this happen in the live version as well? If someone sends a request, the underlying data changes and then resends the same request, get the same answer?

Also shouldn’t a cache be invalidated automatically if the db/ underlying data changes (no matter the expiry date)?

Also here is the list of my middleware, maybe there is something wrong here:

```auto
MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'hq.middlewares.ThreadLocals',
    'models_logging.middleware.LoggingStackMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 18, 2022, 12:38pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/4 "2022-05-18T12:38:22Z")

</div>

> [@creyD](#):
>
> If someone sends a request, the underlying data changes and then resends the same request, get the same answer?

Correct, for the life of the cache.

> [@creyD](#):
>
> Also shouldn’t a cache be invalidated automatically if the db/ underlying data changes (no matter the expiry date)?

Not necessarily - it really depends upon the application. Not all applications require “up-to-the-second” page accuracy.

(There’s also the issue with trying to determine _which_ pages are affected by underlying data changes. If you’re going to invalidate the entire cache based upon any data change, it’s probably not worth caching at all. But, if you want to go that route, you can always implement the per-view cache.)

Cache-control is a **hard** problem, and one not suited for a one-size-fits-all answer.

---

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 18, 2022, 12:55pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/5 "2022-05-18T12:55:49Z")

</div>

What I imagined was a method, where I could manually invalidate the cache for a certain page/ object. Because my data needs to be correct all the time.

I see that this is a hard problem, but I imagine a lot of people having similar problem like mine, where a database call is often repeated by the user (i.e. by refreshing a page) and would be much faster gotten from cache. I suppose the complete manual caching [from here](https://docs.djangoproject.com/en/4.0/topics/cache/#basic-usage) would be the correct interface for this.

There is no interface for invalidating a certain dataset/ page/ model in the cache?

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 18, 2022, 2:14pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/6 "2022-05-18T14:14:35Z")

</div>

> [@creyD](#):
>
> Because my data needs to be correct all the time.

Then don’t cache. Those two requirements are in direct opposition to each other.

> [@creyD](#):
>
> … and would be much faster gotten from cache …

Except the cache needs to know when the data has changed, which means the database needs to be queried _anyway_.

Keep in mind that PostgreSQL is already going to cache as much data as it physically can. Your queries do not necessarily create IO activity by accessing physical disk.

One of my major systems has a 4 GB database - I’ve got the database memory allocated to the level that everything remains memory resident. The level of read requests on the drives is remarkably low.

> [@creyD](#):
>
> I suppose the complete manual caching [from here](https://docs.djangoproject.com/en/4.0/topics/cache/#basic-usage) would be the correct interface for this.

You can build your own cache manager on top of the lower-level API. `<opinion>` Personally, I doubt the value of it. I think your efforts would be better spent tuning other parts of your environment. `</opinion>`

> [@creyD](#):
>
> There is no interface for invalidating a certain dataset/ page/ model in the cache?

The site-cache is a page-level cache indexed (effectively) by URL. I’m not sure that a general solution to identifying which URLs are affected by a data change is practical.

---

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 18, 2022, 2:47pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/7 "2022-05-18T14:47:34Z")

</div>

Thanks for the detailed answers!

> [@KenWhitesell](#):
>
> Except the cache needs to know when the data has changed, which means the database needs to be queried _anyway_ .

I believe it is makes a difference if the database is queried within the view at user runtime or if I would add a signal which updates the cache from the DB in the background. I don’t mind the database query once in a while as the data isn’t changed that often. But when it is changed it shouldn’t be shown incorrectly after.

> [@KenWhitesell](#):
>
> Keep in mind that PostgreSQL is already going to cache as much data as it physically can. Your queries do not necessarily create IO activity by accessing physical disk.

This is not really my problem. My problem is that views/ view sets of my API make a dozen Postgres queries to get all the info (N+1 problem) for one request. Especially with list views this takes a long time. I tried mitigating this with `select_related`/ `prefetch`, which could help alternatively, but it is really messy. I just wanted code-wise a cleaner option, as we would need to maintain the different selection parameters manually from now on.

> [@KenWhitesell](#):
>
> Then don’t cache. Those two requirements are in direct opposition to each other.

I beg to differ here, I believe this depends on the strategy. As far as I understand a Write-Through cache i.e. is supposed to solve exactly this problem.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 18, 2022, 3:15pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/8 "2022-05-18T15:15:03Z")

</div>

> [@creyD](#):
>
> My problem is that views/ view sets of my API make a dozen Postgres queries to get all the info (N+1 problem) for one request.

Fixing _that_ problem is going to pay a lot higher dividends than trying to engineer a caching solution.

> [@creyD](#):
>
> As far as I understand a Write-Through cache i.e. is supposed to solve exactly this problem.

The problem here is the indirect relationship between a URL and the underlying data. A write-through cache is appropriate at the database layer. But there’s no direct link between a URL and database tables. You cannot definitively identify (intrinsically) what URLs will be affected when any specific instance of a Model is updated.

The old joke:

> There are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors.

has more truth to it than not.

Side note: To go back to your original question regarding usage for testing, the docs go on to say:

> Responses to requests for the same URL with different query parameters are considered to be unique pages and are cached separately.

This means that you could generate an arbitrary query variable for your url, ensuring that the test won’t retrieve a cached version.

---

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 18, 2022, 3:36pm UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/9 "2022-05-18T15:36:19Z")

</div>

> [@KenWhitesell](#):
>
> The problem here is the indirect relationship between a URL and the underlying data.

I suppose this is correct and wasn’t entirely clear to me, I expected that there is some kind of connection. Understanding the mechanisms now it is obvious that only this “dumb data” is stored in the cache.

The local test was only to see if there is some kind of performance improvement and to test out the strategy and find errors, like this one. Thanks for all your help!

---

<div class="post-metadata">

**Author:** ![DanielGnzlzVll](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/danielgnzlzvll/32/4585_2.png) [@DanielGnzlzVll](https://forum.djangoproject.com/u/DanielGnzlzVll)\
**Post date:** [May 20, 2022, 2:22am UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/10 "2022-05-20T02:22:15Z")

</div>

Hi, depending on your system requirements you can django-cachalot which is an awesome library

---

<div class="post-metadata">

**Author:** ![creyD](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/creyd/32/8554_2.png) [@creyD](https://forum.djangoproject.com/u/creyD)\
**Post date:** [May 20, 2022, 7:31am UTC](https://forum.djangoproject.com/t/invalidating-cache-doesnt-happen-automatically/13780/11 "2022-05-20T07:31:12Z")

</div>

Thanks a lot for the tip! I am currently testing this, but at the moment I have to say everything looks very good!
