Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added a ReconnectClient that makes the connector prone to server rest…

…arts
  • Loading branch information...
commit 8a589f32e1585134166568e4fcbc2bb1a8153751 1 parent 4e843ab
@tarekziade tarekziade authored
Showing with 99 additions and 3 deletions.
  1. +2 −3 memcachepool/cache.py
  2. +97 −0 memcachepool/client.py
View
5 memcachepool/cache.py
@@ -9,7 +9,6 @@
import time
from django.core.cache.backends.memcached import MemcachedCache
-
from memcachepool.pool import ClientPool
@@ -25,9 +24,9 @@ class UMemcacheCache(MemcachedCache):
_FLAG_LONG = 1 << 2
def __init__(self, server, params):
- import umemcache
+ from memcachepool import client
kls = super(MemcachedCache, self)
- kls.__init__(server, params, library=umemcache,
+ kls.__init__(server, params, library=client,
value_not_found_exception=ValueError)
# see how to pass the pool value
self.maxsize = int(params.get('MAX_POOL_SIZE', 35))
View
97 memcachepool/client.py
@@ -0,0 +1,97 @@
+import socket
+import time
+from errno import EISCONN, EINVAL
+from functools import wraps
+
+from umemcache import Client as OriginalClient
+from umemcache import MemcachedError
+
+
+_RETRY = ('set', 'get', 'gets', 'get_multi', 'gets_multi',
+ 'add', 'replace', 'append', 'prepend', 'delete',
+ 'cas', 'incr', 'decr', 'stats', 'flush_all',
+ 'version')
+
+
+class Client(object):
+ """On connection errors, tries to reconnect
+ """
+ def __init__(self, address, max_item_size=None, max_connect_retries=5,
+ reconnect_delay=.5):
+ self.address = address
+ self.max_item_size = max_item_size
+ self._client = None
+ self.funcs = []
+ self._create_client()
+ self.max_connect_retries = max_connect_retries
+ self.reconnect_delay = reconnect_delay
+
+ def _create_connector(self):
+ if self.max_item_size is not None:
+ self._client = OriginalClient(self.address, self.max_item_size)
+ else:
+ self._client = OriginalClient(self.address)
+
+ self.funcs = [func for func in dir(self._client)
+ if not func.startswith('_')]
+
+ def _create_client(self):
+ reconnect = self._client is not None
+
+ if reconnect:
+ try:
+ self._client.close()
+ except Exception:
+ pass
+
+ self._create_connector()
+
+ if reconnect:
+ retries = 0
+ delay = self.reconnect_delay
+ while retries < self.max_connect_retries:
+ try:
+ self._client.connect()
+ except socket.error, exc:
+ if exc.errno == EISCONN:
+ return # we're good
+ if exc.errno == EINVAL:
+ # we're doomed, retry
+ self._create_connector()
+
+ time.sleep(delay)
+ retries += 1
+ delay *= 2 # growing the delay
+
+ raise exc
+
+ def _with_retry(self, func):
+ @wraps(func)
+ def __with_retry(*args, **kw):
+ retries = 0
+ delay = self.reconnect_delay
+ current_func = func
+
+ while retries < self.max_connect_retries:
+ try:
+ return current_func(*args, **kw)
+ except (IOError, RuntimeError, MemcachedError), exc:
+ self._create_client()
+ current_func = getattr(self._client, func.__name__)
+ time.sleep(delay)
+ retries += 1
+ delay *= 3 # growing the delay
+
+ raise exc
+ return __with_retry
+
+ def __getattr__(self, name):
+ if not name in self.funcs:
+ return self.__dict__[name]
+
+ original = getattr(self._client, name)
+
+ if name in _RETRY:
+ return self._with_retry(original)
+
+ return original
Please sign in to comment.
Something went wrong with that request. Please try again.