[Answered ]-How does django per-site cache work exactly?

3👍

You can look up the source code of process_response in the middleware UpdateCacheMiddleware to figure it out:

def process_response(self, request, response):
    """Sets the cache, if needed."""
    if not self._should_update_cache(request, response):
        # We don't need to update the cache, just return.
        return response

    if response.streaming or response.status_code != 200:
        return response

    # Don't cache responses that set a user-specific (and maybe security
    # sensitive) cookie in response to a cookie-less request.
    if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'):
        return response

    # Try to get the timeout from the "max-age" section of the "Cache-
    # Control" header before reverting to using the default cache_timeout
    # length.
    timeout = get_max_age(response)
    if timeout is None:
        timeout = self.cache_timeout
    elif timeout == 0:
        # max-age was set to 0, don't bother caching.
        return response
    patch_response_headers(response, timeout)
    if timeout:
        cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache)
        if hasattr(response, 'render') and callable(response.render):
            response.add_post_render_callback(
                lambda r: self.cache.set(cache_key, r, timeout)
            )
        else:
            self.cache.set(cache_key, response, timeout)
    return response

First check if your blog is aimed to have authenticated users, as it is it won’t work due to if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie').

If not authenticated then you have to look into learn_cache_key method which computes the key in which the view will be cached:

def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cache=None):
"""
Learns what headers to take into account for some request URL from the
response object. It stores those headers in a global URL registry so that
later access to that URL will know what headers to take into account
without building the response object itself. The headers are named in the
Vary header of the response, but we want to prevent response generation.

The list of headers to use for cache key generation is stored in the same
cache as the pages themselves. If the cache ages some data out of the
cache, this just means that we have to build the response once to get at
the Vary header and so at the list of headers to use for the cache key.
"""
if key_prefix is None:
    key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
if cache_timeout is None:
    cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
cache_key = _generate_cache_header_key(key_prefix, request)
if cache is None:
    cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
if response.has_header('Vary'):
    is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N
    # If i18n or l10n are used, the generated cache key will be suffixed
    # with the current locale. Adding the raw value of Accept-Language is
    # redundant in that case and would result in storing the same content
    # under multiple keys in the cache. See #18191 for details.
    headerlist = []
    for header in cc_delim_re.split(response['Vary']):
        header = header.upper().replace('-', '_')
        if header == 'ACCEPT_LANGUAGE' and is_accept_language_redundant:
            continue
        headerlist.append('HTTP_' + header)
    headerlist.sort()
    cache.set(cache_key, headerlist, cache_timeout)
    return _generate_cache_key(request, request.method, headerlist, key_prefix)
else:
    # if there is no Vary header, we still need a cache key
    # for the request.build_absolute_uri()
    cache.set(cache_key, [], cache_timeout)
    return _generate_cache_key(request, request.method, [], key_prefix)

Note that Django will dig into response['Vary'] header, and will add to the key variations to the key for it.

So fist check if your responses add Vary header, and if so which value it has, if it’s different from before saving the entry you’ll have why is not cached.

Note also that if multilanguage is activated it will also generate different keys per language.

When Django adds or modifies Varyheader?

Here you can see when it happens or how it happens in the docs and how to control cache in views here.

Finally take into account that if you’re using a third-party app for your blog then it probably make use of cache control mechanisms to refresh cache when varying data like updating an entry.

Django version

In Django >=1.7 fully qualified urls are used, so if you use these versions also take into account HOSTS:

Changed in Django 1.7:
Cache keys use the request’s fully-qualified URL rather than just the path and query string.

👤danius

-1👍

Well, turns out my testing has problem. I tested again carefully when no one else visits my site. Here’s the output of memcached -vv:

# first visit

<30 new auto-negotiating client connection
30: Client using the ascii protocol
<30 get :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST
>30 END
<30 set :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST 1 600 14
>30 STORED
<30 set :1:views.decorators.cache.cache_page..GET.f384b899ecab7abd6fb0a567608b97b2.d41d8cd98f00b204e9800998ecf8427e.en-us.CST 1 600 33215
>30 STORED
<30 connection closed.

# second visit

<30 new auto-negotiating client connection
30: Client using the ascii protocol
<30 get :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST
>30 sending key :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST
>30 END
<30 get :1:views.decorators.cache.cache_page..GET.f384b899ecab7abd6fb0a567608b97b2.d41d8cd98f00b204e9800998ecf8427e.en-us.CST
>30 sending key :1:views.decorators.cache.cache_page..GET.f384b899ecab7abd6fb0a567608b97b2.d41d8cd98f00b204e9800998ecf8427e.en-us.CST
>30 END
<30 connection closed.

# modified and save

<30 new auto-negotiating client connection
30: Client using the ascii protocol
<30 get :1:views.decorators.cache.cache_header..7029e9375fc4657a73dae1f9bddb73e5.en-us.CST
>30 END
<30 connection closed.

# visit again

<30 new auto-negotiating client connection
30: Client using the ascii protocol
<30 get :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST
>30 sending key :1:views.decorators.cache.cache_header..f384b899ecab7abd6fb0a567608b97b2.en-us.CST
>30 END
<30 get :1:views.decorators.cache.cache_page..GET.f384b899ecab7abd6fb0a567608b97b2.d41d8cd98f00b204e9800998ecf8427e.en-us.CST
>30 sending key :1:views.decorators.cache.cache_page..GET.f384b899ecab7abd6fb0a567608b97b2.d41d8cd98f00b204e9800998ecf8427e.en-us.CST
>30 END
<30 connection closed. 

This output shows that memcached is working as expected. I’m not sure what’s the explanation for previous output though.

Leave a comment