Skip to content

Instantly share code, notes, and snippets.

@mrts
Created March 16, 2010 23:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrts/334682 to your computer and use it in GitHub Desktop.
Save mrts/334682 to your computer and use it in GitHub Desktop.
"""
Thread-safe Django cache backend for pylibmc.
Tested on Python 2.6, should work on 2.5 as well.
Use it by setting CACHE_BACKEND in settings.py, e.g.:
CACHE_BACKEND = 'projdir.utils.pylibmcd://127.0.0.1:11211/'
And here's how to properly install pylibmcd in Ubuntu for mod_wsgi:
---
VENV_PATH=/some/path/venv
virtualenv --no-site-packages $VENV_PATH
wget -O - http://launchpad.net/libmemcached/1.0/0.41/+download/libmemcached-0.41.tar.gz | tar zxf -
cd libmemcached-0.41
BASE_LIBPATH=$VENV_PATH/lib/share
./configure --prefix $BASE_LIBPATH
make install
export CPATH=$BASE_LIBPATH/include/
export LIBRARY_PATH=$BASE_LIBPATH/lib/
export LD_RUN_PATH=$LIBRARY_PATH
pip --environment=$VENV_PATH install pylibmc
---
The trick lies in setting LD_RUN_PATH so that mod_wsgi can find the shared libs.
"""
from __future__ import with_statement
from django.core.cache.backends.base import BaseCache
from django.utils.encoding import smart_unicode, smart_str
import pylibmc
class CacheClass(BaseCache):
def __init__(self, server, params):
super(CacheClass, self).__init__(params)
mc = pylibmc.Client(server.split(';'))
mc.behaviors = {"tcp_nodelay": True}
self._pool = pylibmc.ThreadMappedPool(mc)
def _call(self, method, *params):
with self._pool.reserve() as mc:
return getattr(mc, method)(*params)
def add(self, key, value, timeout=None):
if isinstance(value, unicode):
value = value.encode('utf-8')
return self._call('add', smart_str(key), value,
self.default_timeout if timeout is None else timeout)
def get(self, key, default=None):
val = self._call('get', smart_str(key))
if val is None:
return default
else:
if isinstance(val, basestring):
return smart_unicode(val)
else:
return val
def set(self, key, value, timeout=None):
if isinstance(value, unicode):
value = value.encode('utf-8')
self._call('set', smart_str(key), value,
self.default_timeout if timeout is None else timeout)
def delete(self, key):
self._call('delete', smart_str(key))
def get_many(self, keys):
return self._call('get_multi', map(smart_str, keys))
def set_many(self, mapping, timeout=None):
return self._call('set_multi',
dict((smart_str(key), val) for key, val in mapping.iteritems()),
self.default_timeout if timeout is None else timeout)
def close(self, **kwargs):
self._pool.master.disconnect_all()
def incr(self, key, delta=1):
return self._call('incr', smart_str(key), delta)
def decr(self, key, delta=1):
return self._call('decr', smart_str(key), delta)
@lericson
Copy link

lericson commented Sep 8, 2010

Neat.

@mrts
Copy link
Author

mrts commented Sep 13, 2010

Thanks :).

@lericson
Copy link

Perhaps this should go into some package or something, or should it simply stay as a Gist until the Django ticket is fixed?

@mrts
Copy link
Author

mrts commented Sep 20, 2010

I'd rather say let it stay as a Gist till then. After all, it's properly documented, no :)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment