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 33a95575fc)
This commit is contained in:
Alexander Makarov 2015-04-06 15:49:41 +03:00
parent cedce339a0
commit 18ca7fabec
2 changed files with 32 additions and 5 deletions

View File

@ -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'])

View File

@ -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)))