Improve check capacity sql
During a benchmark on allocation creation, I found checking allocations capacity occupied a great amount of time when allocation table is fairly big. Add a check on resource provider id to reduce allocation records to be joined with resource providers and inventories. Change-Id: Ic36809fa7161c2aa07a42669b44046993e5db058
This commit is contained in:
parent
888cd51e42
commit
7c01dd1a65
|
@ -1823,11 +1823,12 @@ def _check_capacity_exceeded(ctx, allocs):
|
|||
# SELECT resource_provider_id, resource_class_id, SUM(used) AS used
|
||||
# FROM allocations
|
||||
# WHERE resource_class_id IN ($RESOURCE_CLASSES)
|
||||
# AND resource_provider_id IN ($RESOURCE_PROVIDERS)
|
||||
# GROUP BY resource_provider_id, resource_class_id
|
||||
# ) AS allocs
|
||||
# ON inv.resource_provider_id = allocs.resource_provider_id
|
||||
# AND inv.resource_class_id = allocs.resource_class_id
|
||||
# WHERE rp.uuid IN ($RESOURCE_PROVIDERS)
|
||||
# WHERE rp.id IN ($RESOURCE_PROVIDERS)
|
||||
# AND inv.resource_class_id IN ($RESOURCE_CLASSES)
|
||||
#
|
||||
# We then take the results of the above and determine if any of the
|
||||
|
@ -1835,11 +1836,13 @@ def _check_capacity_exceeded(ctx, allocs):
|
|||
rc_ids = set([_RC_CACHE.id_from_string(a.resource_class)
|
||||
for a in allocs])
|
||||
provider_uuids = set([a.resource_provider.uuid for a in allocs])
|
||||
|
||||
provider_ids = set([a.resource_provider.id for a in allocs])
|
||||
usage = sa.select([_ALLOC_TBL.c.resource_provider_id,
|
||||
_ALLOC_TBL.c.resource_class_id,
|
||||
sql.func.sum(_ALLOC_TBL.c.used).label('used')])
|
||||
usage = usage.where(_ALLOC_TBL.c.resource_class_id.in_(rc_ids))
|
||||
usage = usage.where(
|
||||
sa.and_(_ALLOC_TBL.c.resource_class_id.in_(rc_ids),
|
||||
_ALLOC_TBL.c.resource_provider_id.in_(provider_ids)))
|
||||
usage = usage.group_by(_ALLOC_TBL.c.resource_provider_id,
|
||||
_ALLOC_TBL.c.resource_class_id)
|
||||
usage = sa.alias(usage, name='usage')
|
||||
|
@ -1868,7 +1871,7 @@ def _check_capacity_exceeded(ctx, allocs):
|
|||
|
||||
sel = sa.select(cols_in_output).select_from(primary_join)
|
||||
sel = sel.where(
|
||||
sa.and_(_RP_TBL.c.uuid.in_(provider_uuids),
|
||||
sa.and_(_RP_TBL.c.id.in_(provider_ids),
|
||||
_INV_TBL.c.resource_class_id.in_(rc_ids)))
|
||||
records = ctx.session.execute(sel)
|
||||
# Create a map keyed by (rp_uuid, res_class) for the records in the DB
|
||||
|
|
|
@ -1909,6 +1909,114 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
|
|||
self.ctx, migration_uuid)
|
||||
self.assertEqual(0, len(allocations))
|
||||
|
||||
def test_create_exceeding_capacity_allocation(self):
|
||||
"""Tests on a list of allocations which contains an invalid allocation
|
||||
exceeds resource provider's capacity.
|
||||
|
||||
Expect InvalidAllocationCapacityExceeded to be raised and all
|
||||
allocations in the list should not be applied.
|
||||
|
||||
"""
|
||||
empty_rp = rp_obj.ResourceProvider(
|
||||
context=self.ctx,
|
||||
uuid=uuidsentinel.empty_rp,
|
||||
name=uuidsentinel.empty_rp,
|
||||
)
|
||||
empty_rp.create()
|
||||
|
||||
full_rp = rp_obj.ResourceProvider(
|
||||
context=self.ctx,
|
||||
uuid=uuidsentinel.full_rp,
|
||||
name=uuidsentinel.full_rp,
|
||||
)
|
||||
full_rp.create()
|
||||
|
||||
for rp in (empty_rp, full_rp):
|
||||
cpu_inv = rp_obj.Inventory(
|
||||
context=self.ctx,
|
||||
resource_provider=rp,
|
||||
resource_class=fields.ResourceClass.VCPU,
|
||||
total=24,
|
||||
reserved=0,
|
||||
min_unit=1,
|
||||
max_unit=24,
|
||||
step_size=1,
|
||||
allocation_ratio=16.0)
|
||||
ram_inv = rp_obj.Inventory(
|
||||
context=self.ctx,
|
||||
resource_provider=rp,
|
||||
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||
total=1024,
|
||||
reserved=0,
|
||||
min_unit=64,
|
||||
max_unit=1024,
|
||||
step_size=64,
|
||||
allocation_ratio=1.0)
|
||||
inv_list = rp_obj.InventoryList(context=self.ctx,
|
||||
objects=[cpu_inv, ram_inv])
|
||||
rp.set_inventory(inv_list)
|
||||
|
||||
# First create a allocation to consume full_rp's resource.
|
||||
alloc_list = rp_obj.AllocationList(context=self.ctx,
|
||||
objects=[
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance,
|
||||
resource_provider=full_rp,
|
||||
resource_class=fields.ResourceClass.VCPU,
|
||||
used=12),
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance,
|
||||
resource_provider=full_rp,
|
||||
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||
used=1024)
|
||||
])
|
||||
alloc_list.create_all()
|
||||
|
||||
# Create a allocation list consists of valid requests and a invalid
|
||||
# request exceeds memory full_rp can provide.
|
||||
alloc_list = rp_obj.AllocationList(context=self.ctx,
|
||||
objects=[
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance2,
|
||||
resource_provider=empty_rp,
|
||||
resource_class=fields.ResourceClass.VCPU,
|
||||
used=12),
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance2,
|
||||
resource_provider=empty_rp,
|
||||
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||
used=512),
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance2,
|
||||
resource_provider=full_rp,
|
||||
resource_class=fields.ResourceClass.VCPU,
|
||||
used=12),
|
||||
rp_obj.Allocation(
|
||||
context=self.ctx,
|
||||
consumer_id=uuidsentinel.instance2,
|
||||
resource_provider=full_rp,
|
||||
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||
used=512),
|
||||
])
|
||||
|
||||
self.assertRaises(exception.InvalidAllocationCapacityExceeded,
|
||||
alloc_list.create_all)
|
||||
|
||||
# Make sure that allocations of both empty_rp and full_rp remains
|
||||
# unchanged.
|
||||
allocations = rp_obj.AllocationList.get_all_by_resource_provider(
|
||||
self.ctx, full_rp)
|
||||
self.assertEqual(2, len(allocations))
|
||||
|
||||
allocations = rp_obj.AllocationList.get_all_by_resource_provider(
|
||||
self.ctx, empty_rp)
|
||||
self.assertEqual(0, len(allocations))
|
||||
|
||||
|
||||
class UsageListTestCase(ResourceProviderBaseCase):
|
||||
|
||||
|
|
|
@ -412,6 +412,33 @@ class TestAllocation(test_objects._LocalTest):
|
|||
objects=[obj])
|
||||
self.assertRaises(exception.ObjectActionError, alloc_list.create_all)
|
||||
|
||||
def test_create_exceed_capacity_fails(self):
|
||||
rp = resource_provider.ResourceProvider(context=self.context,
|
||||
uuid=_RESOURCE_PROVIDER_UUID,
|
||||
name=_RESOURCE_PROVIDER_NAME)
|
||||
rp.create()
|
||||
inv = resource_provider.Inventory(context=self.context,
|
||||
resource_provider=rp,
|
||||
resource_class=_RESOURCE_CLASS_NAME,
|
||||
total=16,
|
||||
reserved=2,
|
||||
min_unit=1,
|
||||
max_unit=16,
|
||||
step_size=1,
|
||||
allocation_ratio=1.0)
|
||||
inv_list = resource_provider.InventoryList(context=self.context,
|
||||
objects=[inv])
|
||||
rp.set_inventory(inv_list)
|
||||
obj = resource_provider.Allocation(context=self.context,
|
||||
resource_provider=rp,
|
||||
resource_class=_RESOURCE_CLASS_NAME,
|
||||
consumer_id=uuids.fake_instance,
|
||||
used=16)
|
||||
alloc_list = resource_provider.AllocationList(self.context,
|
||||
objects=[obj])
|
||||
self.assertRaises(exception.InvalidAllocationCapacityExceeded,
|
||||
alloc_list.create_all)
|
||||
|
||||
|
||||
class TestAllocationListNoDB(test_objects._LocalTest):
|
||||
USES_DB = False
|
||||
|
|
Loading…
Reference in New Issue