Commits

Sylvain Hellegouarch committed 3b5e670

caching recipe using a memcached backend

Comments (0)

Files changed (3)

web/caching/memcached_caching/__init__.py

+# -*- coding: utf-8 -*-
+import cherrypy
+
+class Root(object):
+    @cherrypy.expose
+    def index(self):
+        cherrypy.request.cache['count'] += 1
+        return """<html>
+<head />
+<body>
+    <a href="/">Hit me again</a>
+    <p>Cached value: %d</p>
+</body>
+</html>""" % cherrypy.request.cache['count']
+    
+if __name__ == '__main__':
+    cherrypy.config.update({'server.socket_port': 8090})
+    
+    from memcachedplugin import MemcachedPlugin
+    MemcachedPlugin(cherrypy.engine).subscribe()
+
+    from memcachedtool import MemcachedTool
+    cherrypy.tools.memcache = MemcachedTool()
+
+    cherrypy.quickstart(Root(), '', {'/': {'tools.memcache.on': True,
+                                           'tools.memcache.keys': {'count': 0}}})

web/caching/memcached_caching/memcachedplugin.py

+# -*- coding: utf-8 -*-
+import cherrypy
+from cherrypy.process import plugins
+from memcache import Client
+
+__all__ = ['MemcachedPlugin']
+
+class MemcachedPlugin(plugins.SimplePlugin):
+    def __init__(self, bus, nodes='127.0.0.1:11211'):
+        plugins.SimplePlugin.__init__(self, bus)
+        if isinstance(nodes, basestring):
+            nodes = [nodes]
+            
+        self.client = Client(nodes)
+
+    def start(self):
+        """ Add the channels dedicated to the
+        caching API to the bus.
+        """
+        self.bus.log('Setting up the memcached client')
+        self.bus.subscribe("cache-value", self.cache)
+        self.bus.subscribe("get-cached-value", self.get)
+        self.bus.subscribe("del-cached-value", self.delete)
+
+    def stop(self):
+        """ Remove the channels dedicated to the
+        caching API from the bus.
+        """
+        self.bus.log('Releasing the memcached client')
+        self.bus.unsubscribe("cache-value", self.cache)
+        self.bus.unsubscribe("get-cached-value", self.get)
+        self.bus.unsubscribe("del-cached-value", self.delete)
+        self.client = None
+
+    def cache(self, key, value):
+        self.client.set(key, value)
+
+    def get(self, key):
+        return self.client.get(key)
+
+    def delete(self, key):
+        return self.client.delete(key)

web/caching/memcached_caching/memcachedtool.py

+# -*- coding: utf-8 -*-
+import cherrypy
+
+__all__ = ['MemcachedTool']
+
+class MemcachedTool(cherrypy.Tool):
+    def __init__(self):
+        """ Tool that use a memcached cluster as a caching
+        backend for requests to use.
+
+        Enable this tool and provide for each path
+        a suitable set of keys you want to lookup
+        from the cache on each request. These values
+        will be stored into ``cherrypy.request.cache``
+        as a dictionary and available within your
+        page handler. Once the request completes,
+        the values found in ``cherrypy.request.cache``
+        are stored back into the cache.        
+        """
+        cherrypy.Tool.__init__(self, 'before_handler',
+                               self._load_from_cache,
+                               priority=10)
+
+    def _setup(self):
+        cherrypy.Tool._setup(self)
+        cherrypy.request.hooks.attach('before_finalize',
+                                      self._cache_values,
+                                      priority=80)
+ 
+    def _load_from_cache(self, keys):
+        """ Load the given ``keys`` from the cache
+        right before the page handler is called.
+        ``keys`` is a dictionary which provides the
+        default value to use if the cache doesn't hold
+        a given key.
+
+        The cached values are then stored into
+        ``cherrypy.request.cache`` as a dictionary.
+        """
+        values = {}
+        cache = cherrypy.engine.publish
+        for k in keys:
+            v = cache('get-cached-value', k)
+            if v is not []:
+                v = v.pop()
+            if v is None:
+                v = keys[k]
+            values[k] = v
+            
+        cherrypy.request.cache = values
+
+    def _cache_values(self):
+        """ Store the dictionary from
+        ``cherrypy.request.cache`` into the cache
+        every time a request finishes.
+        """
+        try:
+            values = cherrypy.request.cache
+            cache = cherrypy.engine.publish
+            for k in values:
+                cache('cache-value', k, values[k])
+        finally:
+            cherrypy.request.cache = None
+            delattr(cherrypy.request, 'cache')