Don't yield to a provider with unsupported labels
The optimization introduced in Id04f57ff1e4c28357370729b6383f5119cd616dc can lead to a starvation of certain requests under the following conditions: * node request with a requested provider that doesn't support the required node types * another provider that could technically serve the request yields to the requested provider * requested provider is at quota or high inflow of node request * requested provider postpones rejecting the yielded request as it doesn't support the required labels To avoid starvation of those request, we only yield to the requested provider if it is capable of serving the request. This is done by checking the supported labels of the requested provider. Change-Id: I0ded785a13d1f955a71d519dc40e5e5c0ec35043
This commit is contained in:
parent
b13feb7ae8
commit
4f0628c8c2
|
@ -162,8 +162,9 @@ class PoolWorker(threading.Thread, stats.StatsReporter):
|
||||||
if req.provider and req.provider != self.provider_name:
|
if req.provider and req.provider != self.provider_name:
|
||||||
# The request is asking for a specific provider
|
# The request is asking for a specific provider
|
||||||
candidate_launchers = set(
|
candidate_launchers = set(
|
||||||
[x.id for x in launchers
|
x.id for x in launchers
|
||||||
if x.provider_name == req.provider])
|
if x.provider_name == req.provider
|
||||||
|
and x.supported_labels.issuperset(req.node_types))
|
||||||
if candidate_launchers:
|
if candidate_launchers:
|
||||||
# There is a launcher online which can satisfy the request
|
# There is a launcher online which can satisfy the request
|
||||||
if not candidate_launchers.issubset(set(req.declined_by)):
|
if not candidate_launchers.issubset(set(req.declined_by)):
|
||||||
|
|
|
@ -744,6 +744,36 @@ class TestLauncher(tests.DBTestCase):
|
||||||
self.assertEqual(len(req1.nodes), 1)
|
self.assertEqual(len(req1.nodes), 1)
|
||||||
self.zk.getNode(req1.nodes[0])
|
self.zk.getNode(req1.nodes[0])
|
||||||
|
|
||||||
|
def test_node_request_provider_label_mismatch(self):
|
||||||
|
"""Test that a node request for a specific provider is only honored
|
||||||
|
when the requested labels are supported."""
|
||||||
|
configfile = self.setup_config('node.yaml')
|
||||||
|
self.useBuilder(configfile)
|
||||||
|
pool = self.useNodepool(configfile, watermark_sleep=1)
|
||||||
|
pool.start()
|
||||||
|
self.waitForImage('fake-provider', 'fake-image')
|
||||||
|
self.waitForNodes('fake-label', 1)
|
||||||
|
|
||||||
|
# Create a dummy launcher with a different set of supported labels
|
||||||
|
# than what we are going to request.
|
||||||
|
dummy_launcher = zk.Launcher()
|
||||||
|
dummy_launcher.provider_name = 'other-provider'
|
||||||
|
dummy_launcher.supported_labels = {'other-label', }
|
||||||
|
self.zk.registerLauncher(dummy_launcher)
|
||||||
|
|
||||||
|
# Node request for a specific provider that doesn't support the
|
||||||
|
# requested node type.
|
||||||
|
req = zk.NodeRequest()
|
||||||
|
req.state = zk.REQUESTED
|
||||||
|
req.provider = 'other-provider'
|
||||||
|
req.node_types.append('fake-label')
|
||||||
|
self.zk.storeNodeRequest(req)
|
||||||
|
|
||||||
|
req = self.waitForNodeRequest(req)
|
||||||
|
self.assertEqual(req.state, zk.FULFILLED)
|
||||||
|
self.assertEqual(len(req.nodes), 1)
|
||||||
|
self.zk.getNode(req.nodes[0])
|
||||||
|
|
||||||
def test_node_boot_from_volume(self):
|
def test_node_boot_from_volume(self):
|
||||||
"""Test that an image and node are created from a volume"""
|
"""Test that an image and node are created from a volume"""
|
||||||
configfile = self.setup_config('node_boot_from_volume.yaml')
|
configfile = self.setup_config('node_boot_from_volume.yaml')
|
||||||
|
|
Loading…
Reference in New Issue