From c8d7506ce6b555e60a94f217a41f8d397dedf651 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Wed, 9 Sep 2015 10:03:52 +1000 Subject: [PATCH] Identity plugin thread safety A common case is for Nova (or other service) to create an admin authentication from a CONF file and then have many greenlet threads that want to reuse that authentication. If a token expires then many threads all try and fetch a new token to use and can step over each other. I was hoping for a way to put a lock in so that all plugins were thread safe however fixing it for identity plugins solves almost all real world situations and anyone doing non-identity plugins will have to manage threads themselves. Closes-Bug: #1493835 Change-Id: Ie478499a086a4b0db4fb9e5b820f6f5cd4074763 --- keystoneauth1/identity/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/keystoneauth1/identity/base.py b/keystoneauth1/identity/base.py index 416ba87..36c62f5 100644 --- a/keystoneauth1/identity/base.py +++ b/keystoneauth1/identity/base.py @@ -11,6 +11,7 @@ # under the License. import abc +import threading import six @@ -38,6 +39,7 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin): self.reauthenticate = reauthenticate self._endpoint_cache = {} + self._lock = threading.Lock() @abc.abstractmethod def get_auth_ref(self, session, **kwargs): @@ -119,8 +121,14 @@ class BaseIdentityPlugin(plugin.BaseAuthPlugin): :returns: Valid AccessInfo :rtype: :py:class:`keystonauth.access.AccessInfo` """ - if self._needs_reauthenticate(): - self.auth_ref = self.get_auth_ref(session) + # Hey Kids! Thread safety is important particularly in the case where + # a service is creating an admin style plugin that will then proceed + # to make calls from many threads. As a token expires all the threads + # will try and fetch a new token at once, so we want to ensure that + # only one thread tries to actually fetch from keystone at once. + with self._lock: + if self._needs_reauthenticate(): + self.auth_ref = self.get_auth_ref(session) return self.auth_ref