Optimize CountPoolChildrenForQuota task in amphorav2

The CountPoolChildrenForQuota task takes 3 or 4 seconds to execute in
amphorav2. When cascade deleting a load balancer with many pools (60),
it might take minutes.

We don't need to get and to convert all the fields from the object to
compute the number of children of a pool, we can optimize this task by
creating a new method in the PoolRepository class.

Story 2008873
Task 42414

Change-Id: Ie4d40509ae7e6faccecd1d0cc0bc4c7d93959261
(cherry picked from commit 7075d22c4c)
This commit is contained in:
Gregory Thiemonge 2021-04-30 09:21:45 +02:00 committed by Carlos Goncalves
parent 46979a0d1b
commit adb454b2e8
4 changed files with 63 additions and 37 deletions

View File

@ -2873,14 +2873,10 @@ class CountPoolChildrenForQuota(BaseDatabaseTask):
:returns: None
"""
session = db_apis.get_session()
db_pool = self.pool_repo.get(session, id=pool_id)
LOG.debug("Counting pool children for "
"project: %s ", db_pool.project_id)
hm_count, member_count = (
self.pool_repo.get_children_count(session, pool_id))
health_mon_count = 1 if db_pool.health_monitor else 0
member_count = len(db_pool.members)
return {'HM': health_mon_count, 'member': member_count}
return {'HM': hm_count, 'member': member_count}
class DecrementL7policyQuota(BaseDatabaseTask):

View File

@ -1027,6 +1027,16 @@ class PoolRepository(BaseRepository):
session, pagination_helper=pagination_helper,
query_options=query_options, **filters)
def get_children_count(self, session, pool_id):
hm_count = session.query(models.HealthMonitor).filter(
models.HealthMonitor.pool_id == pool_id,
models.HealthMonitor.provisioning_status != consts.DELETED).count()
member_count = session.query(models.Member).filter(
models.Member.pool_id == pool_id,
models.Member.provisioning_status != consts.DELETED).count()
return (hm_count, member_count)
class MemberRepository(BaseRepository):
model_class = models.Member

View File

@ -2666,6 +2666,50 @@ class PoolRepositoryTest(BaseRepositoryTest):
self.assertIsNone(self.hm_repo.get(self.session, pool_id=hm.pool_id))
self.assertIsNone(self.sp_repo.get(self.session, pool_id=sp.pool_id))
def test_get_children_count(self):
pool = self.create_pool(pool_id=self.FAKE_UUID_1,
project_id=self.FAKE_UUID_2)
hm_count, member_count = (
self.pool_repo.get_children_count(self.session, pool.id))
self.assertEqual(0, hm_count)
self.assertEqual(0, member_count)
self.hm_repo.create(self.session, pool_id=pool.id,
type=constants.HEALTH_MONITOR_HTTP,
delay=1, timeout=1, fall_threshold=1,
rise_threshold=1, enabled=True,
provisioning_status=constants.ACTIVE,
operating_status=constants.ONLINE)
hm_count, member_count = (
self.pool_repo.get_children_count(self.session, pool.id))
self.assertEqual(1, hm_count)
self.assertEqual(0, member_count)
self.member_repo.create(self.session, id=self.FAKE_UUID_3,
project_id=self.FAKE_UUID_2,
pool_id=pool.id,
ip_address="192.0.2.1",
protocol_port=80,
provisioning_status=constants.ACTIVE,
operating_status=constants.ONLINE,
enabled=True,
backup=False)
self.member_repo.create(self.session, id=self.FAKE_UUID_4,
project_id=self.FAKE_UUID_2,
pool_id=pool.id,
ip_address="192.0.2.2",
protocol_port=80,
provisioning_status=constants.ACTIVE,
operating_status=constants.ONLINE,
enabled=True,
backup=False)
hm_count, member_count = (
self.pool_repo.get_children_count(self.session, pool.id))
self.assertEqual(1, hm_count)
self.assertEqual(2, member_count)
class MemberRepositoryTest(BaseRepositoryTest):

View File

@ -319,48 +319,24 @@ class TestDatabaseTasksQuota(base.TestCase):
self.assertEqual(1, mock_lock_session.rollback.call_count)
@mock.patch('octavia.db.api.get_session')
@mock.patch('octavia.db.repositories.PoolRepository.get')
@mock.patch('octavia.db.repositories.PoolRepository.get_children_count')
def test_count_pool_children_for_quota(self, repo_mock, session_mock):
project_id = uuidutils.generate_uuid()
member1 = data_models.Member(id=1, project_id=project_id)
member2 = data_models.Member(id=2, project_id=project_id)
healtmon = data_models.HealthMonitor(id=1, project_id=project_id)
pool_no_children = data_models.Pool(id=1, project_id=project_id)
pool_1_mem = data_models.Pool(id=1, project_id=project_id,
members=[member1])
pool_hm = data_models.Pool(id=1, project_id=project_id,
health_monitor=healtmon)
pool_hm_2_mem = data_models.Pool(id=1, project_id=project_id,
health_monitor=healtmon,
members=[member1, member2])
pool = data_models.Pool(id=1, project_id=project_id)
task = database_tasks.CountPoolChildrenForQuota()
# Test pool with no children
repo_mock.reset_mock()
repo_mock.return_value = pool_no_children
result = task.execute(pool_no_children.id)
repo_mock.return_value = (0, 0)
result = task.execute(pool.id)
self.assertEqual({'HM': 0, 'member': 0}, result)
# Test pool with one member
repo_mock.reset_mock()
repo_mock.return_value = pool_1_mem
result = task.execute(pool_1_mem.id)
self.assertEqual({'HM': 0, 'member': 1}, result)
# Test pool with health monitor and no members
repo_mock.reset_mock()
repo_mock.return_value = pool_hm
result = task.execute(pool_hm.id)
self.assertEqual({'HM': 1, 'member': 0}, result)
# Test pool with health monitor and two members
repo_mock.reset_mock()
repo_mock.return_value = pool_hm_2_mem
result = task.execute(pool_hm_2_mem.id)
repo_mock.return_value = (1, 2)
result = task.execute(pool.id)
self.assertEqual({'HM': 1, 'member': 2}, result)