Login

MintCache (simple version)

Author:
disqus
Posted:
June 10, 2008
Language:
Python
Version:
.96
Score:
6 (after 6 ratings)

This is intended as an alternative to http://www.djangosnippets.org/snippets/155/

Put this in your own cache.py and import it instead of django.core.cache and use it the same way. We left out the "add" function but it shouldn't be too hard to make if you want it.

From the above post: "The purpose of this caching scheme is to avoid the dog-pile effect. Dog-piling is what normally happens when your data for the cache takes more time to generate than your server is answering requests per second. In other words if your data takes 5 seconds to generate and you are serving 10 requests per second, then when the data expires the normal cache schemes will spawn 50 attempts a regenerating the data before the first request completes. The increased load from the 49 redundant processes may further increase the time it takes to generate the data. If this happens then you are well on your way into a death spiral

MintCache works to prevent this scenario by using memcached to to keep track of not just an expiration date, but also a stale date The first client to request data past the stale date is asked to refresh the data, while subsequent requests are given the stale but not-yet-expired data as if it were fresh, with the undertanding that it will get refreshed in a 'reasonable' amount of time by that initial request."

 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
"""Wrapper functions around Django's core cache to implement
stale-while-revalidating cache. Has the standard Django cache
interface. The timeout passed to ``set'' is the time at which
the cache will be revalidated; this is different from the 
built-in cache behavior because the object will still be available
from the cache for MINT_DELAY additional seconds.
"""

import time

from django.core.cache import cache

# MINT_DELAY is an upper bound on how long any value should take to 
# be generated (in seconds)
MINT_DELAY = 30 
DEFAULT_TIMEOUT = 300

def get(key):
    packed_val = cache.get(key)
    if packed_val is None:
        return None
    val, refresh_time, refreshed = packed_val
    if (time.time() > refresh_time) and not refreshed:
        # Store the stale value while the cache revalidates for another
        # MINT_DELAY seconds.
        set(key, val, timeout=MINT_DELAY, refreshed=True)
        return None
    return val

def set(key, val, timeout=DEFAULT_TIMEOUT, refreshed=False):
    refresh_time = timeout + time.time()
    real_timeout = timeout + MINT_DELAY
    packed_val = (val, refresh_time, refreshed)
    return cache.set(key, packed_val, real_timeout)

delete = cache.delete

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

scelerat (on June 30, 2009):

I'm looking at line 32(?):

real_timeout = timeout + MINT_DELAY

Combined with line 26, there could be some confusion about how long the delay is:

set(key, val, timeout=MINT_DELAY, refreshed=True)

Right now the delay or stale period is going to be twice whatever MINT_DELAY is since you get

real_timeout = MINT_DELAY + MINT_DELAY

whenever you're in your refresh/stale condition.

Was that intended?

A fix in set() might be

if refreshed:
  real_timeout = timeout
else:
  real_timeout = timeout + MINT_DELAY

#

fahhem (on December 1, 2009):

No, the timeout sent to set() is the timeout for when it becomes stale. The +MINT_DELAY is when it really times out, for when the Django cache returns None.

The code is fine as is, you wouldn't give set timeout=MINT_DELAY unless you just wanted the timeout to be twice MINT_DELAY.

#

andrew (on July 29, 2010):

I wrote this code, and I think scelerat is right, but I could be wrong. Also, 'refreshed' should be renamed 'refreshing', and DEFAULT_TIMEOUT should be fetched from the CAHCE_BACKEND setting.

#

andrew (on August 5, 2010):

Anyway you should probably use django-newcache instead.

#

Please login first before commenting.