Login

Scalable and invalidateble cache_page decorator

Author:
marinho
Posted:
May 6, 2009
Language:
Python
Version:
1.0
Tags:
cache decorator invalidation
Score:
0 (after 0 ratings)

This cache_page decorator can be used in replacement to Django's django.views.decorators.cache.cache_page.

It resolves two problems:

  • invalidation (its cache key is not dependent of header nor request, then you can use model signals (or method 'put' in Google App Engine) to invalidate a URL or a list of them)
  • easier to scale (can be used at once memcached server by many differente servers)

Feel free to show me where it can have problems or limitations.

Updated and improved a lot

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from django.core.cache import cache
from django.template.defaultfilters import slugify
from django.http import HttpResponse
from django.conf import settings

CACHE_KEY_PREFIX = settings.CACHE_MIDDLEWARE_KEY_PREFIX + 'cache-page:'

def invalidate_cache(url, html5=None):
    """Does cache invalidation for a given URL"""
    cache.set(make_cache_key(url, html5), None)

def invalidate_all_cached_pages():
    """Invalidates all cached pages at once."""
    keys = [key for key in cache._cache.keys() if key.startswith(CACHE_KEY_PREFIX)]
    cache.set_many(dict([(key, None) for key in keys]))

def make_cache_key(url, html5=None):
    key = CACHE_KEY_PREFIX + slugify(url)

    if html5 == True:
        key = '.'.join([key, 'html5'])
    elif html5 == False:
        key = '.'.join([key, 'html4'])

    return key

def cache_page(arg=None): # 1 day as default
    """This decorator works in replacement to Django's decorator with same name, but
    it doesn't use Django's middleware system to make cache key, so, it uses its own
    logic to do it and make possible invalidate cache.
    
    This decorator shouldn't be used to views with user-based data."""

    default_exp = 60 * 30 # 30 minutes

    def _wrapper(func):
        def _inner(request, *args, **kwargs):
            key = make_cache_key(request.get_full_path(), getattr(request, 'supports_html5', None))
            content = cache.get(key, None)

            if not content:
                resp = func(request, *args, **kwargs)
                content = resp.content

                # Only stores in cache if is a regular HttpResponse returning text/html
                if resp.__class__.__name__ == 'HttpResponse' and\
                   getattr(resp, 'mimetype', 'text/html') == 'text/html':
                    cache.set(key, content, expiration)
                else:
                    return resp

            return HttpResponse(content)
        return _inner

    if callable(arg):
        expiration = default_exp
        return _wrapper(arg)
    else:
        expiration = arg or default_exp 
        return _wrapper

More like this

  1. Cache Any Function by jeffwheeler 8 years, 1 month ago
  2. Get the Django decorator/middleware cache key for given URL by s29 3 years, 5 months ago
  3. Sorl Thumbnail + Amazon S3 by skoczen 5 years, 10 months ago
  4. Caching tag with singnal-based invalidation by rushman 7 years, 1 month ago
  5. Use memcached to throttle POSTs by coleifer 4 years, 11 months ago

Comments

Please login first before commenting.