0
Ukryte przez Django ficzery memcached
Będzie szybko, bo do napisania tego posta zbieram się już zbyt długo :)
Django dostarcza kilka różnych backendów cache, w tym także do memcached. Sęk w tym, że ogranicza przy tym pewne funkcje m.in. kompresje.
Pisząc własny backend możemy ustawić binarny protokół pikli, to nam zaoszczędzi trochę transferu. To samo możemy zrobić z kompresją. W przypadku korzystania z python-memcached można podać minimalny rozmiar obiektu, powyżej którego biblioteka użyje zliba, aby poupychać trochę bity ;) Kolejny ficzer, który był mi potrzebny w pracy to możliwość wymuszenia, na który node ma trafić wybrany klucz. Jak się okazało to również obsługuje python-memcached (cmemcached sie wywala jak dostanie tuplę jako klucz). Wystarczy jako klucz przekazać dwu elementową tuplę. Pierwszy element to numeryczny hash klucza, drugi to nazwa klucza. Wybranie serwera na podstawie hasha jest banalne.
Ponizej znajduje sie moj backend dla Django.
Django dostarcza kilka różnych backendów cache, w tym także do memcached. Sęk w tym, że ogranicza przy tym pewne funkcje m.in. kompresje.
Pisząc własny backend możemy ustawić binarny protokół pikli, to nam zaoszczędzi trochę transferu. To samo możemy zrobić z kompresją. W przypadku korzystania z python-memcached można podać minimalny rozmiar obiektu, powyżej którego biblioteka użyje zliba, aby poupychać trochę bity ;) Kolejny ficzer, który był mi potrzebny w pracy to możliwość wymuszenia, na który node ma trafić wybrany klucz. Jak się okazało to również obsługuje python-memcached (cmemcached sie wywala jak dostanie tuplę jako klucz). Wystarczy jako klucz przekazać dwu elementową tuplę. Pierwszy element to numeryczny hash klucza, drugi to nazwa klucza. Wybranie serwera na podstawie hasha jest banalne.
hash % number_of_nodes
Ponizej znajduje sie moj backend dla Django.
import logging import pickle import zlib from django.conf import settings from django.core.cache.backends.base import InvalidCacheBackendError from django.core.cache.backends.memcached import CacheClass as MemcCacheClass from django.utils.encoding import smart_str as _smart_str from django.utils.encoding import smart_unicode log = logging.getLogger(__name__) try: import memcache except: raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' library") def yamb_smart_str(s): if isinstance(s, tuple): serverhash, key = s key = _smart_str(key) return (serverhash, key) else: return _smart_str(s) class CacheClass(MemcCacheClass): def __init__(self, server, params): MemcCacheClass.__init__(self, server, params) self._cache = memcache.Client(server.split(';'), \ pickleProtocol=getattr(settings, 'CACHE_PICKLE_PROTOCOL', 0)) def _server_hash(self, key): if isinstance(key, tuple): srv, k = key else: srv, k = '-', key return srv, k def _pickled_size(self, value): pickled_size = 0 if settings.DEBUG or (log.getEffectiveLevel() <= logging.DEBUG): pickled = pickle.dumps(value, getattr(settings, 'CACHE_PICKLE_PROTOCOL', 0)) pickled_size = len(pickled) if getattr(settings, 'CACHE_MIN_COMPRESSION_LEN', 0) >= pickled_size: pickled = zlib.compress(pickled) pickled_size = len(pickled) del pickled return pickled_size def add(self, key, value, timeout=0): if isinstance(value, unicode): value = value.encode('utf-8') return self._cache.add(yamb_smart_str(key), value, timeout or self.default_timeout) def get(self, key, default=None): value = self._cache.get(yamb_smart_str(key)) srv, k = self._server_hash(key) pickled_size = self._pickled_size(value) if value is None: log.debug("MISS %s %s %s", k, srv, pickled_size) else: log.debug("HIT %s %s %s", k, srv, pickled_size) if value is None: return default else: if isinstance(value, basestring): return smart_unicode(value) else: return value def set(self, key, value, timeout=0): print "tyeaa" if isinstance(value, unicode): value = value.encode('utf-8') srv, k = self._server_hash(key) pickled_size = self._pickled_size(value) log.debug("SET %s %s %s", k, srv, pickled_size) self._cache.set(yamb_smart_str(key), value, timeout or self.default_timeout, \ getattr(settings, 'CACHE_MIN_COMPRESSION_LEN', 0)) def delete(self, key): srv, k = self._server_hash(key) log.debug("DELETE %s %s -", k, srv) self._cache.delete(yamb_smart_str(key)) def get_many(self, keys): return self._cache.get_multi(map(yamb_smart_str, keys)) def incr(self, key, delta=1): srv, k = self._server_hash(key) log.debug("INCR %s %s -", k, srv) return self._cache.incr(key, delta) def decr(self, key, delta=1): srv, k = self._server_hash(key) log.debug("DECR %s %s -", k, srv) return self._cache.decr(key, delta)
Prześlij komentarz