Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1066)

Unified Diff: third_party/google-endpoints/dogpile/cache/backends/memcached.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/google-endpoints/dogpile/cache/backends/memcached.py
diff --git a/third_party/google-endpoints/dogpile/cache/backends/memcached.py b/third_party/google-endpoints/dogpile/cache/backends/memcached.py
new file mode 100644
index 0000000000000000000000000000000000000000..6758a998024ea22fa28ce38521954292061d0d55
--- /dev/null
+++ b/third_party/google-endpoints/dogpile/cache/backends/memcached.py
@@ -0,0 +1,364 @@
+"""
+Memcached Backends
+------------------
+
+Provides backends for talking to `memcached <http://memcached.org>`_.
+
+"""
+
+from ..api import CacheBackend, NO_VALUE
+from ...util import compat
+from ... import util
+import random
+import time
+
+__all__ = 'GenericMemcachedBackend', 'MemcachedBackend',\
+ 'PylibmcBackend', 'BMemcachedBackend', 'MemcachedLock'
+
+
+class MemcachedLock(object):
+ """Simple distributed lock using memcached.
+
+ This is an adaptation of the lock featured at
+ http://amix.dk/blog/post/19386
+
+ """
+
+ def __init__(self, client_fn, key, timeout=0):
+ self.client_fn = client_fn
+ self.key = "_lock" + key
+ self.timeout = timeout
+
+ def acquire(self, wait=True):
+ client = self.client_fn()
+ i = 0
+ while True:
+ if client.add(self.key, 1, self.timeout):
+ return True
+ elif not wait:
+ return False
+ else:
+ sleep_time = (((i + 1) * random.random()) + 2 ** i) / 2.5
+ time.sleep(sleep_time)
+ if i < 15:
+ i += 1
+
+ def release(self):
+ client = self.client_fn()
+ client.delete(self.key)
+
+
+class GenericMemcachedBackend(CacheBackend):
+ """Base class for memcached backends.
+
+ This base class accepts a number of paramters
+ common to all backends.
+
+ :param url: the string URL to connect to. Can be a single
+ string or a list of strings. This is the only argument
+ that's required.
+ :param distributed_lock: boolean, when True, will use a
+ memcached-lock as the dogpile lock (see :class:`.MemcachedLock`).
+ Use this when multiple
+ processes will be talking to the same memcached instance.
+ When left at False, dogpile will coordinate on a regular
+ threading mutex.
+ :param lock_timeout: integer, number of seconds after acquiring a lock that
+ memcached should expire it. This argument is only valid when
+ ``distributed_lock`` is ``True``.
+
+ .. versionadded:: 0.5.7
+
+ :param memcached_expire_time: integer, when present will
+ be passed as the ``time`` parameter to ``pylibmc.Client.set``.
+ This is used to set the memcached expiry time for a value.
+
+ .. note::
+
+ This parameter is **different** from Dogpile's own
+ ``expiration_time``, which is the number of seconds after
+ which Dogpile will consider the value to be expired.
+ When Dogpile considers a value to be expired,
+ it **continues to use the value** until generation
+ of a new value is complete, when using
+ :meth:`.CacheRegion.get_or_create`.
+ Therefore, if you are setting ``memcached_expire_time``, you'll
+ want to make sure it is greater than ``expiration_time``
+ by at least enough seconds for new values to be generated,
+ else the value won't be available during a regeneration,
+ forcing all threads to wait for a regeneration each time
+ a value expires.
+
+ The :class:`.GenericMemachedBackend` uses a ``threading.local()``
+ object to store individual client objects per thread,
+ as most modern memcached clients do not appear to be inherently
+ threadsafe.
+
+ In particular, ``threading.local()`` has the advantage over pylibmc's
+ built-in thread pool in that it automatically discards objects
+ associated with a particular thread when that thread ends.
+
+ """
+
+ set_arguments = {}
+ """Additional arguments which will be passed
+ to the :meth:`set` method."""
+
+ def __init__(self, arguments):
+ self._imports()
+ # using a plain threading.local here. threading.local
+ # automatically deletes the __dict__ when a thread ends,
+ # so the idea is that this is superior to pylibmc's
+ # own ThreadMappedPool which doesn't handle this
+ # automatically.
+ self.url = util.to_list(arguments['url'])
+ self.distributed_lock = arguments.get('distributed_lock', False)
+ self.lock_timeout = arguments.get('lock_timeout', 0)
+ self.memcached_expire_time = arguments.get(
+ 'memcached_expire_time', 0)
+
+ def has_lock_timeout(self):
+ return self.lock_timeout != 0
+
+ def _imports(self):
+ """client library imports go here."""
+ raise NotImplementedError()
+
+ def _create_client(self):
+ """Creation of a Client instance goes here."""
+ raise NotImplementedError()
+
+ @util.memoized_property
+ def _clients(self):
+ backend = self
+
+ class ClientPool(compat.threading.local):
+ def __init__(self):
+ self.memcached = backend._create_client()
+
+ return ClientPool()
+
+ @property
+ def client(self):
+ """Return the memcached client.
+
+ This uses a threading.local by
+ default as it appears most modern
+ memcached libs aren't inherently
+ threadsafe.
+
+ """
+ return self._clients.memcached
+
+ def get_mutex(self, key):
+ if self.distributed_lock:
+ return MemcachedLock(lambda: self.client, key,
+ timeout=self.lock_timeout)
+ else:
+ return None
+
+ def get(self, key):
+ value = self.client.get(key)
+ if value is None:
+ return NO_VALUE
+ else:
+ return value
+
+ def get_multi(self, keys):
+ values = self.client.get_multi(keys)
+ return [
+ NO_VALUE if key not in values
+ else values[key] for key in keys
+ ]
+
+ def set(self, key, value):
+ self.client.set(
+ key,
+ value,
+ **self.set_arguments
+ )
+
+ def set_multi(self, mapping):
+ self.client.set_multi(
+ mapping,
+ **self.set_arguments
+ )
+
+ def delete(self, key):
+ self.client.delete(key)
+
+ def delete_multi(self, keys):
+ self.client.delete_multi(keys)
+
+
+class MemcacheArgs(object):
+ """Mixin which provides support for the 'time' argument to set(),
+ 'min_compress_len' to other methods.
+
+ """
+ def __init__(self, arguments):
+ self.min_compress_len = arguments.get('min_compress_len', 0)
+
+ self.set_arguments = {}
+ if "memcached_expire_time" in arguments:
+ self.set_arguments["time"] = arguments["memcached_expire_time"]
+ if "min_compress_len" in arguments:
+ self.set_arguments["min_compress_len"] = \
+ arguments["min_compress_len"]
+ super(MemcacheArgs, self).__init__(arguments)
+
+pylibmc = None
+
+
+class PylibmcBackend(MemcacheArgs, GenericMemcachedBackend):
+ """A backend for the
+ `pylibmc <http://sendapatch.se/projects/pylibmc/index.html>`_
+ memcached client.
+
+ A configuration illustrating several of the optional
+ arguments described in the pylibmc documentation::
+
+ from dogpile.cache import make_region
+
+ region = make_region().configure(
+ 'dogpile.cache.pylibmc',
+ expiration_time = 3600,
+ arguments = {
+ 'url':["127.0.0.1"],
+ 'binary':True,
+ 'behaviors':{"tcp_nodelay": True,"ketama":True}
+ }
+ )
+
+ Arguments accepted here include those of
+ :class:`.GenericMemcachedBackend`, as well as
+ those below.
+
+ :param binary: sets the ``binary`` flag understood by
+ ``pylibmc.Client``.
+ :param behaviors: a dictionary which will be passed to
+ ``pylibmc.Client`` as the ``behaviors`` parameter.
+ :param min_compress_len: Integer, will be passed as the
+ ``min_compress_len`` parameter to the ``pylibmc.Client.set``
+ method.
+
+ """
+
+ def __init__(self, arguments):
+ self.binary = arguments.get('binary', False)
+ self.behaviors = arguments.get('behaviors', {})
+ super(PylibmcBackend, self).__init__(arguments)
+
+ def _imports(self):
+ global pylibmc
+ import pylibmc # noqa
+
+ def _create_client(self):
+ return pylibmc.Client(
+ self.url,
+ binary=self.binary,
+ behaviors=self.behaviors
+ )
+
+memcache = None
+
+
+class MemcachedBackend(MemcacheArgs, GenericMemcachedBackend):
+ """A backend using the standard
+ `Python-memcached <http://www.tummy.com/Community/software/\
+ python-memcached/>`_
+ library.
+
+ Example::
+
+ from dogpile.cache import make_region
+
+ region = make_region().configure(
+ 'dogpile.cache.memcached',
+ expiration_time = 3600,
+ arguments = {
+ 'url':"127.0.0.1:11211"
+ }
+ )
+
+ """
+ def _imports(self):
+ global memcache
+ import memcache # noqa
+
+ def _create_client(self):
+ return memcache.Client(self.url)
+
+
+bmemcached = None
+
+
+class BMemcachedBackend(GenericMemcachedBackend):
+ """A backend for the
+ `python-binary-memcached <https://github.com/jaysonsantos/\
+ python-binary-memcached>`_
+ memcached client.
+
+ This is a pure Python memcached client which
+ includes the ability to authenticate with a memcached
+ server using SASL.
+
+ A typical configuration using username/password::
+
+ from dogpile.cache import make_region
+
+ region = make_region().configure(
+ 'dogpile.cache.bmemcached',
+ expiration_time = 3600,
+ arguments = {
+ 'url':["127.0.0.1"],
+ 'username':'scott',
+ 'password':'tiger'
+ }
+ )
+
+ Arguments which can be passed to the ``arguments``
+ dictionary include:
+
+ :param username: optional username, will be used for
+ SASL authentication.
+ :param password: optional password, will be used for
+ SASL authentication.
+
+ """
+ def __init__(self, arguments):
+ self.username = arguments.get('username', None)
+ self.password = arguments.get('password', None)
+ super(BMemcachedBackend, self).__init__(arguments)
+
+ def _imports(self):
+ global bmemcached
+ import bmemcached
+
+ class RepairBMemcachedAPI(bmemcached.Client):
+ """Repairs BMemcached's non-standard method
+ signatures, which was fixed in BMemcached
+ ef206ed4473fec3b639e.
+
+ """
+
+ def add(self, key, value, timeout=0):
+ try:
+ return super(RepairBMemcachedAPI, self).add(
+ key, value, timeout)
+ except ValueError:
+ return False
+
+ self.Client = RepairBMemcachedAPI
+
+ def _create_client(self):
+ return self.Client(
+ self.url,
+ username=self.username,
+ password=self.password
+ )
+
+ def delete_multi(self, keys):
+ """python-binary-memcached api does not implements delete_multi"""
+ for key in keys:
+ self.delete(key)

Powered by Google App Engine
This is Rietveld 408576698