Login

cache_smart template tag

Author:
michiel_1981
Posted:
February 24, 2008
Language:
Python
Version:
.96
Tags:
template cache memcached
Score:
2 (after 2 ratings)

cache_smart template tag is a drop in replacement for default cache tag by Django but with the added bonus to be more resistant against dog-pile/stampeding effect.

This snippet uses a extra cache entry to store a stale time so we don't have to pickle/unpickle to store this extra value. If this cache entry returns None, as in expired it will reset the stale timeout 30 seconds in the future so further calls will just return the old value while this request is regenerating the new value.

warning Don't use both cache template tags!

 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
61
62
63
64
65
66
67
68
69
70
from django.template import Library, Node, TemplateSyntaxError
from django.template import resolve_variable
from django.core.cache import cache
from django.utils.encoding import force_unicode

register = Library()

class SmartCacheNode(Node):
    STALE_REFRESH = 1
    STALE_CREATED = 2
    def __init__(self, nodelist, expire_time, fragment_name, vary_on):
        self.nodelist = nodelist
        self.stale_time = expire_time
        self.expire_time = expire_time + 300 # create a window to refresh
        self.fragment_name = fragment_name
        self.vary_on = vary_on

    def render(self, context):
        # Build a unicode key for this fragment and all vary-on's.
        cache_key = u':'.join([self.fragment_name] + \
            [force_unicode(resolve_variable(var, context)) for var in self.vary_on])
        cache_key_stale = cache_key + '.stale'
        value = cache.get(cache_key)
        stale = cache.get(cache_key_stale)
        if stale is None:
            cache.set(cache_key_stale, self.STALE_REFRESH, 30) # lock
            value = None # force refresh
        if value is None:
            value = self.nodelist.render(context)
            cache.set(cache_key, value, self.expire_time)
            cache.set(cache_key_stale, self.STALE_CREATED, self.stale_time) # reset        return value


def do_smart_cache(parser, token):
    """
    This will cache the contents of a template fragment for a given amount
    of time, but with the extra bonus of limiting the dog-pile/stampeding
    effect.

    You can easily replace the default template cache, just change the load
    statement from ``{% load cache %}`` to ``{% load cache_smart %}``.

    Usage::

        {% load cache_smart %}
        {% cache [expire_time] [fragment_name] %}
            .. some expensive processing ..
        {% endcache %}

    This tag also supports varying by a list of arguments::

        {% load cache_smart %}
        {% cache [expire_time] [fragment_name] [var1] [var2] .. %}
            .. some expensive processing ..
        {% endcache %}

    Each unique set of arguments will result in a unique cache entry.
    """
    nodelist = parser.parse(('endcache',))
    parser.delete_first_token()
    tokens = token.contents.split()
    if len(tokens) < 3:
        raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0])
    try:
        expire_time = int(tokens[1])
    except ValueError:
        raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1]))
    return SmartCacheNode(nodelist, expire_time, tokens[2], tokens[3:])

register.tag('cache', do_smart_cache)

More like this

  1. Use memcached to throttle POSTs by coleifer 4 years, 11 months ago
  2. Extended db cache backend with 'filter() / LIKE' support (and now scheduled cache clean!) by sleepycal 5 years, 3 months ago
  3. Default Template Loading by nirvdrum 7 years, 9 months ago
  4. Silently-failing include tag by brutasse 4 years, 10 months ago
  5. MintCache by gfranxman 8 years ago

Comments

Please login first before commenting.