0

Ukryte przez Django ficzery memcached

Posted by M on 22:56 in ,
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.

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)

0 Comments

Prześlij komentarz

Copyright © 2009 tech.matee.net All rights reserved. Theme by Laptop Geek. | Bloggerized by FalconHive.