Don't use taskmanagers in builder

ProviderManager is a TaskManager, and TaskManagers are intended
to serialize API requests to a single cloud from multiple threads.
Currently each worker in the builder has its own set of
ProviderManagers.  That means that we are performing cloud API calls
in parallel.  That's probably okay since we perform very few of them,
mostly image uploads and deletes.  And in fact, we probably want
to avoid blocking on image uploads.

However, there is a thread associated with each of these
ProviderManagers, and even though they are idle, in aggregate they
add up to a significant CPU cost.

This makes the use of a TaskManager by a ProviderManager optional
and sets the builder not to use it in order to avoid spawning these
useless threads.

Change-Id: Iaf6498c34a38c384b85d3ab568c43dab0bcdd3d5
This commit is contained in:
James E. Blair 2016-12-01 13:50:06 -08:00
parent 6dcb96310f
commit fe153656df
3 changed files with 40 additions and 21 deletions

View File

@ -464,7 +464,8 @@ class CleanupWorker(BaseWorker):
'''
new_config = nodepool_config.loadConfig(self._config_path)
self._checkForZooKeeperChanges(new_config)
provider_manager.ProviderManager.reconfigure(self._config, new_config)
provider_manager.ProviderManager.reconfigure(self._config, new_config,
use_taskmanager=False)
self._config = new_config
self._cleanup()
@ -947,7 +948,8 @@ class UploadWorker(BaseWorker):
'''
new_config = nodepool_config.loadConfig(self._config_path)
self._checkForZooKeeperChanges(new_config)
provider_manager.ProviderManager.reconfigure(self._config, new_config)
provider_manager.ProviderManager.reconfigure(self._config, new_config,
use_taskmanager=False)
self._config = new_config
self._checkForProviderUploads()

View File

@ -45,18 +45,18 @@ class NotFound(Exception):
pass
def get_provider_manager(provider):
def get_provider_manager(provider, use_taskmanager):
if (provider.cloud_config.get_auth_args().get('auth_url') == 'fake'):
return FakeProviderManager(provider)
return FakeProviderManager(provider, use_taskmanager)
else:
return ProviderManager(provider)
return ProviderManager(provider, use_taskmanager)
class ProviderManager(TaskManager):
class ProviderManager(object):
log = logging.getLogger("nodepool.ProviderManager")
@staticmethod
def reconfigure(old_config, new_config):
def reconfigure(old_config, new_config, use_taskmanager=True):
stop_managers = []
for p in new_config.providers.values():
oldmanager = None
@ -71,7 +71,7 @@ class ProviderManager(TaskManager):
ProviderManager.log.debug("Creating new ProviderManager object"
" for %s" % p.name)
new_config.provider_managers[p.name] = \
get_provider_manager(p)
get_provider_manager(p, use_taskmanager)
new_config.provider_managers[p.name].start()
for stop_manager in stop_managers:
@ -83,14 +83,28 @@ class ProviderManager(TaskManager):
m.stop()
m.join()
def __init__(self, provider):
super(ProviderManager, self).__init__(None, provider.name,
provider.rate)
def __init__(self, provider, use_taskmanager):
self.provider = provider
self.resetClient()
self._images = {}
self._networks = {}
self.__flavors = {}
self._use_taskmanager = use_taskmanager
self._taskmanager = None
def start(self):
if self._use_taskmanager:
self._taskmanager = TaskManager(None, self.provider.name,
self.provider.rate)
self._taskmanager.start()
self.resetClient()
def stop(self):
if self._taskmanager:
self._taskmanager.stop()
def join(self):
if self._taskmanager:
self._taskmanager.join()
@property
def _flavors(self):
@ -99,19 +113,19 @@ class ProviderManager(TaskManager):
return self.__flavors
def _getClient(self):
if self._use_taskmanager:
manager = self._taskmanager
else:
manager = None
return shade.OpenStackCloud(
cloud_config=self.provider.cloud_config,
manager=self,
manager=manager,
**self.provider.cloud_config.config)
def runTask(self, task):
# Run the given task in the TaskManager passed to shade. It turns
# out that this provider manager is the TaskManager we pass, so
# this is a way of running each cloud operation in its own thread
task.run(self._client)
def resetClient(self):
self._client = self._getClient()
if self._use_taskmanager:
self._taskmanager.setClient(self._client)
def _getFlavors(self):
flavors = self.listFlavors()
@ -347,9 +361,9 @@ class ProviderManager(TaskManager):
class FakeProviderManager(ProviderManager):
def __init__(self, provider):
def __init__(self, provider, use_taskmanager):
self.__client = fakeprovider.FakeOpenStackCloud()
super(FakeProviderManager, self).__init__(provider)
super(FakeProviderManager, self).__init__(provider, use_taskmanager)
def _getClient(self):
return self.__client

View File

@ -74,6 +74,9 @@ class TaskManager(threading.Thread):
self._client = None
self.statsd = stats.get_client()
def setClient(self, client):
self._client = client
def stop(self):
self._running = False
self.queue.put(None)