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

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

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 """
2 Memcached Backends
3 ------------------
4
5 Provides backends for talking to `memcached <http://memcached.org>`_.
6
7 """
8
9 from ..api import CacheBackend, NO_VALUE
10 from ...util import compat
11 from ... import util
12 import random
13 import time
14
15 __all__ = 'GenericMemcachedBackend', 'MemcachedBackend',\
16 'PylibmcBackend', 'BMemcachedBackend', 'MemcachedLock'
17
18
19 class MemcachedLock(object):
20 """Simple distributed lock using memcached.
21
22 This is an adaptation of the lock featured at
23 http://amix.dk/blog/post/19386
24
25 """
26
27 def __init__(self, client_fn, key, timeout=0):
28 self.client_fn = client_fn
29 self.key = "_lock" + key
30 self.timeout = timeout
31
32 def acquire(self, wait=True):
33 client = self.client_fn()
34 i = 0
35 while True:
36 if client.add(self.key, 1, self.timeout):
37 return True
38 elif not wait:
39 return False
40 else:
41 sleep_time = (((i + 1) * random.random()) + 2 ** i) / 2.5
42 time.sleep(sleep_time)
43 if i < 15:
44 i += 1
45
46 def release(self):
47 client = self.client_fn()
48 client.delete(self.key)
49
50
51 class GenericMemcachedBackend(CacheBackend):
52 """Base class for memcached backends.
53
54 This base class accepts a number of paramters
55 common to all backends.
56
57 :param url: the string URL to connect to. Can be a single
58 string or a list of strings. This is the only argument
59 that's required.
60 :param distributed_lock: boolean, when True, will use a
61 memcached-lock as the dogpile lock (see :class:`.MemcachedLock`).
62 Use this when multiple
63 processes will be talking to the same memcached instance.
64 When left at False, dogpile will coordinate on a regular
65 threading mutex.
66 :param lock_timeout: integer, number of seconds after acquiring a lock that
67 memcached should expire it. This argument is only valid when
68 ``distributed_lock`` is ``True``.
69
70 .. versionadded:: 0.5.7
71
72 :param memcached_expire_time: integer, when present will
73 be passed as the ``time`` parameter to ``pylibmc.Client.set``.
74 This is used to set the memcached expiry time for a value.
75
76 .. note::
77
78 This parameter is **different** from Dogpile's own
79 ``expiration_time``, which is the number of seconds after
80 which Dogpile will consider the value to be expired.
81 When Dogpile considers a value to be expired,
82 it **continues to use the value** until generation
83 of a new value is complete, when using
84 :meth:`.CacheRegion.get_or_create`.
85 Therefore, if you are setting ``memcached_expire_time``, you'll
86 want to make sure it is greater than ``expiration_time``
87 by at least enough seconds for new values to be generated,
88 else the value won't be available during a regeneration,
89 forcing all threads to wait for a regeneration each time
90 a value expires.
91
92 The :class:`.GenericMemachedBackend` uses a ``threading.local()``
93 object to store individual client objects per thread,
94 as most modern memcached clients do not appear to be inherently
95 threadsafe.
96
97 In particular, ``threading.local()`` has the advantage over pylibmc's
98 built-in thread pool in that it automatically discards objects
99 associated with a particular thread when that thread ends.
100
101 """
102
103 set_arguments = {}
104 """Additional arguments which will be passed
105 to the :meth:`set` method."""
106
107 def __init__(self, arguments):
108 self._imports()
109 # using a plain threading.local here. threading.local
110 # automatically deletes the __dict__ when a thread ends,
111 # so the idea is that this is superior to pylibmc's
112 # own ThreadMappedPool which doesn't handle this
113 # automatically.
114 self.url = util.to_list(arguments['url'])
115 self.distributed_lock = arguments.get('distributed_lock', False)
116 self.lock_timeout = arguments.get('lock_timeout', 0)
117 self.memcached_expire_time = arguments.get(
118 'memcached_expire_time', 0)
119
120 def has_lock_timeout(self):
121 return self.lock_timeout != 0
122
123 def _imports(self):
124 """client library imports go here."""
125 raise NotImplementedError()
126
127 def _create_client(self):
128 """Creation of a Client instance goes here."""
129 raise NotImplementedError()
130
131 @util.memoized_property
132 def _clients(self):
133 backend = self
134
135 class ClientPool(compat.threading.local):
136 def __init__(self):
137 self.memcached = backend._create_client()
138
139 return ClientPool()
140
141 @property
142 def client(self):
143 """Return the memcached client.
144
145 This uses a threading.local by
146 default as it appears most modern
147 memcached libs aren't inherently
148 threadsafe.
149
150 """
151 return self._clients.memcached
152
153 def get_mutex(self, key):
154 if self.distributed_lock:
155 return MemcachedLock(lambda: self.client, key,
156 timeout=self.lock_timeout)
157 else:
158 return None
159
160 def get(self, key):
161 value = self.client.get(key)
162 if value is None:
163 return NO_VALUE
164 else:
165 return value
166
167 def get_multi(self, keys):
168 values = self.client.get_multi(keys)
169 return [
170 NO_VALUE if key not in values
171 else values[key] for key in keys
172 ]
173
174 def set(self, key, value):
175 self.client.set(
176 key,
177 value,
178 **self.set_arguments
179 )
180
181 def set_multi(self, mapping):
182 self.client.set_multi(
183 mapping,
184 **self.set_arguments
185 )
186
187 def delete(self, key):
188 self.client.delete(key)
189
190 def delete_multi(self, keys):
191 self.client.delete_multi(keys)
192
193
194 class MemcacheArgs(object):
195 """Mixin which provides support for the 'time' argument to set(),
196 'min_compress_len' to other methods.
197
198 """
199 def __init__(self, arguments):
200 self.min_compress_len = arguments.get('min_compress_len', 0)
201
202 self.set_arguments = {}
203 if "memcached_expire_time" in arguments:
204 self.set_arguments["time"] = arguments["memcached_expire_time"]
205 if "min_compress_len" in arguments:
206 self.set_arguments["min_compress_len"] = \
207 arguments["min_compress_len"]
208 super(MemcacheArgs, self).__init__(arguments)
209
210 pylibmc = None
211
212
213 class PylibmcBackend(MemcacheArgs, GenericMemcachedBackend):
214 """A backend for the
215 `pylibmc <http://sendapatch.se/projects/pylibmc/index.html>`_
216 memcached client.
217
218 A configuration illustrating several of the optional
219 arguments described in the pylibmc documentation::
220
221 from dogpile.cache import make_region
222
223 region = make_region().configure(
224 'dogpile.cache.pylibmc',
225 expiration_time = 3600,
226 arguments = {
227 'url':["127.0.0.1"],
228 'binary':True,
229 'behaviors':{"tcp_nodelay": True,"ketama":True}
230 }
231 )
232
233 Arguments accepted here include those of
234 :class:`.GenericMemcachedBackend`, as well as
235 those below.
236
237 :param binary: sets the ``binary`` flag understood by
238 ``pylibmc.Client``.
239 :param behaviors: a dictionary which will be passed to
240 ``pylibmc.Client`` as the ``behaviors`` parameter.
241 :param min_compress_len: Integer, will be passed as the
242 ``min_compress_len`` parameter to the ``pylibmc.Client.set``
243 method.
244
245 """
246
247 def __init__(self, arguments):
248 self.binary = arguments.get('binary', False)
249 self.behaviors = arguments.get('behaviors', {})
250 super(PylibmcBackend, self).__init__(arguments)
251
252 def _imports(self):
253 global pylibmc
254 import pylibmc # noqa
255
256 def _create_client(self):
257 return pylibmc.Client(
258 self.url,
259 binary=self.binary,
260 behaviors=self.behaviors
261 )
262
263 memcache = None
264
265
266 class MemcachedBackend(MemcacheArgs, GenericMemcachedBackend):
267 """A backend using the standard
268 `Python-memcached <http://www.tummy.com/Community/software/\
269 python-memcached/>`_
270 library.
271
272 Example::
273
274 from dogpile.cache import make_region
275
276 region = make_region().configure(
277 'dogpile.cache.memcached',
278 expiration_time = 3600,
279 arguments = {
280 'url':"127.0.0.1:11211"
281 }
282 )
283
284 """
285 def _imports(self):
286 global memcache
287 import memcache # noqa
288
289 def _create_client(self):
290 return memcache.Client(self.url)
291
292
293 bmemcached = None
294
295
296 class BMemcachedBackend(GenericMemcachedBackend):
297 """A backend for the
298 `python-binary-memcached <https://github.com/jaysonsantos/\
299 python-binary-memcached>`_
300 memcached client.
301
302 This is a pure Python memcached client which
303 includes the ability to authenticate with a memcached
304 server using SASL.
305
306 A typical configuration using username/password::
307
308 from dogpile.cache import make_region
309
310 region = make_region().configure(
311 'dogpile.cache.bmemcached',
312 expiration_time = 3600,
313 arguments = {
314 'url':["127.0.0.1"],
315 'username':'scott',
316 'password':'tiger'
317 }
318 )
319
320 Arguments which can be passed to the ``arguments``
321 dictionary include:
322
323 :param username: optional username, will be used for
324 SASL authentication.
325 :param password: optional password, will be used for
326 SASL authentication.
327
328 """
329 def __init__(self, arguments):
330 self.username = arguments.get('username', None)
331 self.password = arguments.get('password', None)
332 super(BMemcachedBackend, self).__init__(arguments)
333
334 def _imports(self):
335 global bmemcached
336 import bmemcached
337
338 class RepairBMemcachedAPI(bmemcached.Client):
339 """Repairs BMemcached's non-standard method
340 signatures, which was fixed in BMemcached
341 ef206ed4473fec3b639e.
342
343 """
344
345 def add(self, key, value, timeout=0):
346 try:
347 return super(RepairBMemcachedAPI, self).add(
348 key, value, timeout)
349 except ValueError:
350 return False
351
352 self.Client = RepairBMemcachedAPI
353
354 def _create_client(self):
355 return self.Client(
356 self.url,
357 username=self.username,
358 password=self.password
359 )
360
361 def delete_multi(self, keys):
362 """python-binary-memcached api does not implements delete_multi"""
363 for key in keys:
364 self.delete(key)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698