summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Makarov <amakarov@mirantis.com>2015-04-06 15:49:41 +0300
committerAlexander Makarov <amakarov@mirantis.com>2015-04-20 08:05:07 +0000
commit18ca7fabece4837ad56e435bc9d5f0b6278fa4be (patch)
tree7e90cd1e5799280b3cf1e3d06138ddf1011d0160
parentcedce339a08d475617c7f57c148e192dc3709a34 (diff)
downloadkeystone-18ca7fabece4837ad56e435bc9d5f0b6278fa4be.tar.gz
Make memcache client reusable across threads
memcache.Client is inherited from threading._local so instances are only accessible from current thread or eventlet. Present workaround broke inheritance chain so super() call is unusable. This patch makes artificial client class mimic inheritance from threading._local while using generic object methods allowing reusability. Change-Id: Ic5d5709695877afb995fd816bb0e4ce711b99b60 Closes-Bug: #1440493 (cherry picked from commit 33a95575fc3778bf8ef054f7b9d24fcb7c75100b)
-rw-r--r--keystone/common/cache/_memcache_pool.py21
-rw-r--r--keystone/tests/unit/common/test_connection_pool.py16
2 files changed, 32 insertions, 5 deletions
diff --git a/keystone/common/cache/_memcache_pool.py b/keystone/common/cache/_memcache_pool.py
index b15332db0..bc559781a 100644
--- a/keystone/common/cache/_memcache_pool.py
+++ b/keystone/common/cache/_memcache_pool.py
@@ -35,11 +35,22 @@ from keystone.i18n import _
LOG = log.getLogger(__name__)
-# This 'class' is taken from http://stackoverflow.com/a/22520633/238308
-# Don't inherit client from threading.local so that we can reuse clients in
-# different threads
-_MemcacheClient = type('_MemcacheClient', (object,),
- dict(memcache.Client.__dict__))
+
+class _MemcacheClient(memcache.Client):
+ """Thread global memcache client
+
+ As client is inherited from threading.local we have to restore object
+ methods overloaded by threading.local so we can reuse clients in
+ different threads
+ """
+ __delattr__ = object.__delattr__
+ __getattribute__ = object.__getattribute__
+ __new__ = object.__new__
+ __setattr__ = object.__setattr__
+
+ def __del__(self):
+ pass
+
_PoolItem = collections.namedtuple('_PoolItem', ['ttl', 'connection'])
diff --git a/keystone/tests/unit/common/test_connection_pool.py b/keystone/tests/unit/common/test_connection_pool.py
index 74d0420ce..3813e0339 100644
--- a/keystone/tests/unit/common/test_connection_pool.py
+++ b/keystone/tests/unit/common/test_connection_pool.py
@@ -10,9 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+import threading
import time
import mock
+import six
from six.moves import queue
import testtools
from testtools import matchers
@@ -117,3 +119,17 @@ class TestConnectionPool(core.TestCase):
# after it is available.
connection_pool.put_nowait(conn)
_acquire_connection()
+
+
+class TestMemcacheClientOverrides(core.BaseTestCase):
+
+ def test_client_stripped_of_threading_local(self):
+ """threading.local overrides are restored for _MemcacheClient"""
+ client_class = _memcache_pool._MemcacheClient
+ # get the genuine thread._local from MRO
+ thread_local = client_class.__mro__[2]
+ self.assertTrue(thread_local is threading.local)
+ for field in six.iterkeys(thread_local.__dict__):
+ if field not in ('__dict__', '__weakref__'):
+ self.assertNotEqual(id(getattr(thread_local, field, None)),
+ id(getattr(client_class, field, None)))