Django – sharing a memcached instance

Update: Some Curious User brought to my attention, that a ticket has been opened which, when implemented, will add a setting for a cache prefix. It will also allow other cache key manipulations.

Until recently I’ve been using the file:// django cache, but that has a “problem” when multiple users needs to manipulate the cache (think uid 80 writes a key, that uid 1000 wants to delete).

My problem with the memcached:// django cache provider has been, that it cannot handle being used on a shared memcached instance, because of the danger of key collissions.

If project A and project B would share a memcached instance, they basiclly share the same global namespace. So if they both write a key called actor there is no telling what will happen.

So I wrote a little cache backend for django, that uses the current memcached backend, but adds a pre-defined prefix to all keys.

Usage: Put the code somewhere inside your project in a file called memcached_key_prefix.py, and set your CACHE_BACKEND to something like: path.to.memcached_key_prefix:///127.0.0.1:11211/?key_prefix=sewc_&foo=bar&timeout=3600

"Memcached cache backend with key prefixing"
 
from django.core.cache.backends.base import InvalidCacheBackendError
from django.core.cache.backends.memcached import CacheClass as MemcachedCacheClass
from django.utils.encoding import smart_unicode, smart_str
 
class CacheClass(MemcachedCacheClass):
    def __init__(self, server, params):
        try:
            self._key_prefix = smart_str(params['key_prefix'])
        except KeyError:
            raise InvalidCacheBackendError('key_prefix not specified')
 
        super(CacheClass, self).__init__(server, params)
 
    def _get_key(self, key):
        return self._key_prefix + smart_str(key)
 
    def add(self, key, value, timeout=0):
        return super(CacheClass, self).add(self._get_key(key), value, timeout)
 
    def get(self, key, default=None):
        return super(CacheClass, self).get(self._get_key(key), default)
 
    def set(self, key, value, timeout=0):
        return super(CacheClass, self).set(self._get_key(key), value, timeout)
 
    def delete(self, key):
        return super(CacheClass, self).delete(self._get_key(key))
 
    def get_many(self, keys):
        keys = [self._get_key(key) for key in keys]
        return super(CacheClass, self).get_many(keys)
 
    def incr(self, key, delta=1):
        return super(CacheClass, self).incr(self._get_key(key), delta)
 
    def decr(self, key, delta=1):
        return super(CacheClass, self).decr(self._get_key(key), delta)
This entry was posted in Django, Programming, Python, Work and tagged , , , . Bookmark the permalink.

6 Responses to Django – sharing a memcached instance

  1. Nope, that setting is only used in the cache middleware.

    The above solution is for the cache layer in general, so it will actually kind of conflict with the CACHE_MIDDLEWARE_KEY_PREFIX.

  2. Curious User says:

    Please explain “kind of conflict”.

    I appreciate you taking the time to post this, as have countless other non-commenters.

  3. If you set CACHE_MIDDLEWARE_KEY_PREFIX and use the memcached_key_prefix cache backend, both will add a key to the cache key.

    So, if the CacheMiddleware were to insert a key called frontpage, it would then prefix it with the above setting, and pass it along to the cache backend, which then again prefixes the key. You then end up with a key called CACHE_MIDDLEWARE_KEY_PREFIX + cache_backend_prefix + cache_key which is suboptimal.

  4. Curious User says:

    Thanks for your quick reply!

    Note a patch has been submitted: http://code.djangoproject.com/ticket/13795

  5. Oh, that’s even better!

    I’ll stick it in the post, thanks again :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">