1============ 2Usage Guide 3============ 4 5Overview 6======== 7 8At the time of this writing, popular key/value servers include 9`Memcached <http://memcached.org>`_, `Redis <http://redis.io/>`_ and many others. 10While these tools all have different usage focuses, they all have in common that the storage model 11is based on the retrieval of a value based on a key; as such, they are all potentially 12suitable for caching, particularly Memcached which is first and foremost designed for 13caching. 14 15With a caching system in mind, dogpile.cache provides an interface to a particular Python API 16targeted at that system. 17 18A dogpile.cache configuration consists of the following components: 19 20* A *region*, which is an instance of :class:`.CacheRegion`, and defines the configuration 21 details for a particular cache backend. The :class:`.CacheRegion` can be considered 22 the "front end" used by applications. 23* A *backend*, which is an instance of :class:`.CacheBackend`, describing how values 24 are stored and retrieved from a backend. This interface specifies only 25 :meth:`~.CacheBackend.get`, :meth:`~.CacheBackend.set` and :meth:`~.CacheBackend.delete`. 26 The actual kind of :class:`.CacheBackend` in use for a particular :class:`.CacheRegion` 27 is determined by the underlying Python API being used to talk to the cache, such 28 as Pylibmc. The :class:`.CacheBackend` is instantiated behind the scenes and 29 not directly accessed by applications under normal circumstances. 30* Value generation functions. These are user-defined functions that generate 31 new values to be placed in the cache. While dogpile.cache offers the usual 32 "set" approach of placing data into the cache, the usual mode of usage is to only instruct 33 it to "get" a value, passing it a *creation function* which will be used to 34 generate a new value if and only if one is needed. This "get-or-create" pattern 35 is the entire key to the "Dogpile" system, which coordinates a single value creation 36 operation among many concurrent get operations for a particular key, eliminating 37 the issue of an expired value being redundantly re-generated by many workers simultaneously. 38 39Rudimentary Usage 40================= 41 42dogpile.cache includes a Pylibmc backend. A basic configuration looks like:: 43 44 from dogpile.cache import make_region 45 46 region = make_region().configure( 47 'dogpile.cache.pylibmc', 48 expiration_time = 3600, 49 arguments = { 50 'url': ["127.0.0.1"], 51 } 52 ) 53 54 @region.cache_on_arguments() 55 def load_user_info(user_id): 56 return some_database.lookup_user_by_id(user_id) 57 58.. sidebar:: pylibmc 59 60 In this section, we're illustrating Memcached usage 61 using the `pylibmc <http://pypi.python.org/pypi/pylibmc>`_ backend, which is a high performing 62 Python library for Memcached. It can be compared to the `python-memcached <http://pypi.python.org/pypi/python-memcached>`_ 63 client, which is also an excellent product. Pylibmc is written against Memcached's native API 64 so is markedly faster, though might be considered to have rougher edges. The API is actually a bit 65 more verbose to allow for correct multithreaded usage. 66 67 68Above, we create a :class:`.CacheRegion` using the :func:`.make_region` function, then 69apply the backend configuration via the :meth:`.CacheRegion.configure` method, which returns the 70region. The name of the backend is the only argument required by :meth:`.CacheRegion.configure` 71itself, in this case ``dogpile.cache.pylibmc``. However, in this specific case, the ``pylibmc`` 72backend also requires that the URL of the memcached server be passed within the ``arguments`` dictionary. 73 74The configuration is separated into two sections. Upon construction via :func:`.make_region`, 75the :class:`.CacheRegion` object is available, typically at module 76import time, for usage in decorating functions. Additional configuration details passed to 77:meth:`.CacheRegion.configure` are typically loaded from a configuration file and therefore 78not necessarily available until runtime, hence the two-step configurational process. 79 80Key arguments passed to :meth:`.CacheRegion.configure` include *expiration_time*, which is the expiration 81time passed to the Dogpile lock, and *arguments*, which are arguments used directly 82by the backend - in this case we are using arguments that are passed directly 83to the pylibmc module. 84 85Region Configuration 86==================== 87 88The :func:`.make_region` function currently calls the :class:`.CacheRegion` constructor directly. 89 90.. autoclass:: dogpile.cache.region.CacheRegion 91 :noindex: 92 93One you have a :class:`.CacheRegion`, the :meth:`.CacheRegion.cache_on_arguments` method can 94be used to decorate functions, but the cache itself can't be used until 95:meth:`.CacheRegion.configure` is called. The interface for that method is as follows: 96 97.. automethod:: dogpile.cache.region.CacheRegion.configure 98 :noindex: 99 100The :class:`.CacheRegion` can also be configured from a dictionary, using the :meth:`.CacheRegion.configure_from_config` 101method: 102 103.. automethod:: dogpile.cache.region.CacheRegion.configure_from_config 104 :noindex: 105 106 107Using a Region 108============== 109 110The :class:`.CacheRegion` object is our front-end interface to a cache. It includes 111the following methods: 112 113 114.. automethod:: dogpile.cache.region.CacheRegion.get 115 :noindex: 116 117.. automethod:: dogpile.cache.region.CacheRegion.get_or_create 118 :noindex: 119 120.. automethod:: dogpile.cache.region.CacheRegion.set 121 :noindex: 122 123.. automethod:: dogpile.cache.region.CacheRegion.delete 124 :noindex: 125 126.. automethod:: dogpile.cache.region.CacheRegion.cache_on_arguments 127 :noindex: 128 129.. _creating_backends: 130 131Creating Backends 132================= 133 134Backends are located using the setuptools entrypoint system. To make life easier 135for writers of ad-hoc backends, a helper function is included which registers any 136backend in the same way as if it were part of the existing sys.path. 137 138For example, to create a backend called ``DictionaryBackend``, we subclass 139:class:`.CacheBackend`:: 140 141 from dogpile.cache.api import CacheBackend, NO_VALUE 142 143 class DictionaryBackend(CacheBackend): 144 def __init__(self, arguments): 145 self.cache = {} 146 147 def get(self, key): 148 return self.cache.get(key, NO_VALUE) 149 150 def set(self, key, value): 151 self.cache[key] = value 152 153 def delete(self, key): 154 self.cache.pop(key) 155 156Then make sure the class is available underneath the entrypoint 157``dogpile.cache``. If we did this in a ``setup.py`` file, it would be 158in ``setup()`` as:: 159 160 entry_points=""" 161 [dogpile.cache] 162 dictionary = mypackage.mybackend:DictionaryBackend 163 """ 164 165Alternatively, if we want to register the plugin in the same process 166space without bothering to install anything, we can use ``register_backend``:: 167 168 from dogpile.cache import register_backend 169 170 register_backend("dictionary", "mypackage.mybackend", "DictionaryBackend") 171 172Our new backend would be usable in a region like this:: 173 174 from dogpile.cache import make_region 175 176 region = make_region("myregion") 177 178 region.configure("dictionary") 179 180 data = region.set("somekey", "somevalue") 181 182The values we receive for the backend here are instances of 183``CachedValue``. This is a tuple subclass of length two, of the form:: 184 185 (payload, metadata) 186 187Where "payload" is the thing being cached, and "metadata" is information 188we store in the cache - a dictionary which currently has just the "creation time" 189and a "version identifier" as key/values. If the cache backend requires serialization, 190pickle or similar can be used on the tuple - the "metadata" portion will always 191be a small and easily serializable Python structure. 192 193 194.. _changing_backend_behavior: 195 196Changing Backend Behavior 197========================= 198 199The :class:`.ProxyBackend` is a decorator class provided to easily augment existing 200backend behavior without having to extend the original class. Using a decorator 201class is also adventageous as it allows us to share the altered behavior between 202different backends. 203 204Proxies are added to the :class:`.CacheRegion` object using the :meth:`.CacheRegion.configure` 205method. Only the overridden methods need to be specified and the real backend can 206be accessed with the ``self.proxied`` object from inside the :class:`.ProxyBackend`. 207 208For example, a simple class to log all calls to ``.set()`` would look like this:: 209 210 from dogpile.cache.proxy import ProxyBackend 211 212 import logging 213 log = logging.getLogger(__name__) 214 215 class LoggingProxy(ProxyBackend): 216 def set(self, key, value): 217 log.debug('Setting Cache Key: %s' % key) 218 self.proxied.set(key, value) 219 220 221:class:`.ProxyBackend` can be be configured to optionally take arguments (as long as the 222:meth:`.ProxyBackend.__init__` method is called properly, either directly 223or via ``super()``. In the example 224below, the ``RetryDeleteProxy`` class accepts a ``retry_count`` parameter 225on initialization. In the event of an exception on delete(), it will retry 226this many times before returning:: 227 228 from dogpile.cache.proxy import ProxyBackend 229 230 class RetryDeleteProxy(ProxyBackend): 231 def __init__(self, retry_count=5): 232 super(RetryDeleteProxy, self).__init__() 233 self.retry_count = retry_count 234 235 def delete(self, key): 236 retries = self.retry_count 237 while retries > 0: 238 retries -= 1 239 try: 240 self.proxied.delete(key) 241 return 242 243 except: 244 pass 245 246The ``wrap`` parameter of the :meth:`.CacheRegion.configure` accepts a list 247which can contain any combination of instantiated proxy objects 248as well as uninstantiated proxy classes. 249Putting the two examples above together would look like this:: 250 251 from dogpile.cache import make_region 252 253 retry_proxy = RetryDeleteProxy(5) 254 255 region = make_region().configure( 256 'dogpile.cache.pylibmc', 257 expiration_time = 3600, 258 arguments = { 259 'url':["127.0.0.1"], 260 }, 261 wrap = [ LoggingProxy, retry_proxy ] 262 ) 263 264In the above example, the ``LoggingProxy`` object would be instantated by the 265:class:`.CacheRegion` and applied to wrap requests on behalf of 266the ``retry_proxy`` instance; that proxy in turn wraps 267requests on behalf of the original dogpile.cache.pylibmc backend. 268 269.. versionadded:: 0.4.4 Added support for the :class:`.ProxyBackend` class. 270 271 272Configuring Logging 273==================== 274 275.. versionadded:: 0.9.0 276 277:class:`.CacheRegion` includes logging facilities that will emit debug log 278messages when key cache events occur, including when keys are regenerated as 279well as when hard invalidations occur. Using the `Python logging 280<https://docs.python.org/3/library/logging.html>`_ module, set the log level to 281``dogpile.cache`` to ``logging.DEBUG``:: 282 283 logging.basicConfig() 284 logging.getLogger("dogpile.cache").setLevel(logging.DEBUG) 285 286Debug logging will indicate time spent regenerating keys as well as when 287keys are missing:: 288 289 DEBUG:dogpile.cache.region:No value present for key: '__main__:load_user_info|2' 290 DEBUG:dogpile.cache.region:No value present for key: '__main__:load_user_info|1' 291 DEBUG:dogpile.cache.region:Cache value generated in 0.501 seconds for keys: ['__main__:load_user_info|2', '__main__:load_user_info|3', '__main__:load_user_info|4', '__main__:load_user_info|5'] 292 DEBUG:dogpile.cache.region:Hard invalidation detected for key: '__main__:load_user_info|3' 293 DEBUG:dogpile.cache.region:Hard invalidation detected for key: '__main__:load_user_info|2'