Cache service availability results per request

Retrieving the list of available resource types seems to be inordinately
slow on at least some clouds (though not in our gate tests). It appears
that checking service availability is the main culprit. These service
availability checks are also performed during validation of a template,
so they should be as fast as possible.

Previously, we never cached the availability of a service, probably on
the assumption that checking the catalog, once retrieved, would be
quick. It's not clear that we even need to use the user's token to
retrieve the catalog on each request, since all users should see the
same catalog. However, at a minimum we can cache the results for the
request so that we don't need to do multiple lookups of the same service
when validating a single stack or listing the available resource types.
This patch does that, and also caches the results of extension lookups
in Neutron and Cinder for the duration of the request, independently of
the global oslo.cache settings (which, if enabled, can cache the results
across requests).

Change-Id: I0618dc2a35f7323abedccb13ef3d6537eef1d24c
Task: 37974
This commit is contained in:
Zane Bitter 2020-01-07 11:37:17 -05:00
parent 5e5d97c2b3
commit 758866d890
4 changed files with 38 additions and 18 deletions

View File

@ -49,6 +49,7 @@ class ClientPlugin(object):
self._context = weakref.ref(context)
self._clients = weakref.ref(context.clients)
self._client_instances = {}
self._endpoint_existence = {}
@property
def context(self):
@ -163,14 +164,18 @@ class ClientPlugin(object):
def does_endpoint_exist(self,
service_type,
service_name):
endpoint_type = self._get_client_option(service_name,
'endpoint_type')
try:
self.url_for(service_type=service_type,
endpoint_type=endpoint_type)
return True
except exceptions.EndpointNotFound:
return False
endpoint_key = (service_type, service_name)
if endpoint_key not in self._endpoint_existence:
endpoint_type = self._get_client_option(service_name,
'endpoint_type')
try:
self.url_for(service_type=service_type,
endpoint_type=endpoint_type)
self._endpoint_existence[endpoint_key] = True
except exceptions.EndpointNotFound:
self._endpoint_existence[endpoint_key] = False
return self._endpoint_existence[endpoint_key]
def retry_if_connection_err(exception):

View File

@ -11,6 +11,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
from oslo_cache import core
from oslo_config import cfg
@ -25,3 +29,20 @@ MEMOIZE_FINDER = core.get_memoization_decorator(
conf=cfg.CONF,
region=cache.get_cache_region(),
group="resource_finder_cache")
@six.add_metaclass(abc.ABCMeta)
class ExtensionMixin(object):
def __init__(self, *args, **kwargs):
super(ExtensionMixin, self).__init__(*args, **kwargs)
self._extensions = None
@abc.abstractmethod
def _list_extensions(self):
return []
def has_extension(self, alias):
"""Check if specific extension is present."""
if self._extensions is None:
self._extensions = set(self._list_extensions())
return alias in self._extensions

View File

@ -29,7 +29,8 @@ LOG = logging.getLogger(__name__)
CLIENT_NAME = 'cinder'
class CinderClientPlugin(client_plugin.ClientPlugin):
class CinderClientPlugin(os_client.ExtensionMixin,
client_plugin.ClientPlugin):
exceptions_module = exceptions
@ -76,10 +77,6 @@ class CinderClientPlugin(client_plugin.ClientPlugin):
extensions = self.client().list_extensions.show_all()
return set(extension.alias for extension in extensions)
def has_extension(self, alias):
"""Check if specific extension is present."""
return alias in self._list_extensions()
def get_volume(self, volume):
try:
return self.client().volumes.get(volume)

View File

@ -23,7 +23,8 @@ from heat.engine.clients import client_plugin
from heat.engine.clients import os as os_client
class NeutronClientPlugin(client_plugin.ClientPlugin):
class NeutronClientPlugin(os_client.ExtensionMixin,
client_plugin.ClientPlugin):
exceptions_module = exceptions
@ -117,10 +118,6 @@ class NeutronClientPlugin(client_plugin.ClientPlugin):
extensions = self.client().list_extensions().get('extensions')
return set(extension.get('alias') for extension in extensions)
def has_extension(self, alias):
"""Check if specific extension is present."""
return alias in self._list_extensions()
def _resolve(self, props, key, id_key, key_type):
if props.get(key):
props[id_key] = self.find_resourceid_by_name_or_id(key_type,