skip to Main Content

I’m trying to use a Memcached instance of AWS ElastiCache with a Django project. It seems to be caching a view for a user, but if you come in on a different PC, it isn’t cached until called from that PC (or same PC with different browser).

I’m not sure what I’ve got wrong.

Within settings.py I have

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': os.environ.get('CACHE_LOCATION','127.0.0.1:11211'),
    }
}

MIDDLEWARE = [
    'core.middleware.DenyIndexMiddleware',
    'core.middleware.XForwardedForMiddleware',
    'core.middleware.PrimaryHostRedirectMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
    'masquerade.middleware.MasqueradeMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.contrib.sites.middleware.CurrentSiteMiddleware',
    'cms.middleware.user.CurrentUserMiddleware',
    'cms.middleware.page.CurrentPageMiddleware',
    'cms.middleware.toolbar.ToolbarMiddleware',
    'cms.middleware.language.LanguageCookieMiddleware',
    'cms.middleware.utils.ApphookReloadMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

I’ve then cached the views using cache_page

path('<str:service_type>/<str:location>/', cache_page(60*60)(views.canonical_search), name="canonical-search"),

How do I cache the site so that the page is cached irrespective of the user?

EDIT
I’ve noticed that it never caches when using the user is logged in.

3

Answers


  1. Watch out for the Vary header, that cache_page() takes into account.

    Usually, some middlewares may add a Vary header, for example :

    • CsrfViewMiddleware adds Cookie,
    • GZipMiddlewareadds Accept-Encoding
    • LanguageCookieMiddleware may add Accept-Language

    meaning that as soon as you have a different Cookie (session), encoding, or language, you have a different version of cache for your page.

    As for you case, the CsrfViewMiddleware may be the problem, you can add the decorator @csrf_exempt to your view so that the Vary: Cookie header is not set in the response.

    More info at https://docs.djangoproject.com/en/3.0/topics/cache/#using-vary-headers

    Login or Signup to reply.
  2. Hm, at first I wondered if you’re running into some cache default limitations. You are not using OPTIONS in your CACHE backend definition, so the cache is limited to 300 entries per default.

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': os.environ.get('CACHE_LOCATION','127.0.0.1:11211'),
            'OPTIONS': {
                'MAX_ENTRIES': 1000
            }
    
        }
    }
    

    The next possible problem, that we also had, is that the cache_key generation takes the full QUERY_STRING into account, so (the ?param=bla). But you already stated that the url is the same for all users.

    Next up, as SebCorbin correctly identified, are the potential Vary issues.

    UpdateCacheMiddleware will never cache a cookie-setting response to a cookie-less request.

        def process_response(self, request, 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
    
            # ...
    
    

    The order of execution of middlewares is top-to-bottom for process_request and bottom-to-top for process_response.

    I suspect that one of lower middlewares (or the view) is setting a cookie, so you might be able to work around this issue by moving the 'django.middleware.cache.UpdateCacheMiddleware' below the offending middleware, but you risk loosing features if you don’t move feature-middlewares like LocaleMiddleware as well.

    If your view code is setting cookies you’ll need to switch to the low-level cache API to cache costly operations (or move the cookie logic into a middleware that lives above the UpdateCacheMiddleware middleware).

    Login or Signup to reply.
  3. Though Django documentation, you can read this:
    Django’s cache framework

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
    

    If you need to store a page in the local

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': 'c:/foo/bar',
        }
    }
    

    But, my recommendation is store the results (database result, assets, etc), as @PVSK show in this thread:

    from django.core.cache import cache    
    def sample(request):
            cached_data = cache.get_many(['query1', 'query2'])
            if cached_data:
                return render(request, 'sample.html', {'query1': cached_data['query1'], 'query2': cached_data['query2']})
            else:
                queryset1 = Model.objects.all()
                queryset2 = Model2.objects.all()
                cache.set_many({'query1': queryset1 , 'query2': queryset2 }, None)
                return render(request, 'sample.html', {'query1': queryset1 , 'query2': queryset2})
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search