Reduce the number of Instance.get_by_uuid calls

Only call Instance.get_by_uuid once per migration in
ResourceTracker._update_usage_from_migrations rather than twice in some
cases. Also increased test coverage for
ResourceTracker._update_usage_from_migrations to 100%.

Closes-Bug: #1385489
Change-Id: Ibc7e48340e103f8dcc502f7e91d8ffd939349486
This commit is contained in:
Diana Clarke 2015-09-10 22:05:46 -04:00
parent 917932460a
commit e1e47cb5e6
2 changed files with 114 additions and 10 deletions

View File

@ -746,36 +746,37 @@ class ResourceTracker(object):
self.tracked_migrations[uuid] = (migration, itype)
def _update_usage_from_migrations(self, context, migrations):
self.tracked_migrations.clear()
filtered = {}
instances = {}
self.tracked_migrations.clear()
# do some defensive filtering against bad migrations records in the
# database:
for migration in migrations:
uuid = migration.instance_uuid
try:
instance = migration.instance
if uuid not in instances:
instances[uuid] = migration.instance
except exception.InstanceNotFound as e:
# migration referencing deleted instance
LOG.debug('Migration instance not found: %s', e)
continue
uuid = instance.uuid
# skip migration if instance isn't in a resize state:
if not _instance_in_resize_state(instance):
if not _instance_in_resize_state(instances[uuid]):
LOG.warning(_LW("Instance not resizing, skipping migration."),
instance_uuid=uuid)
continue
# filter to most recently updated migration for each instance:
m = filtered.get(uuid, None)
if not m or migration.updated_at >= m.updated_at:
other_migration = filtered.get(uuid, None)
if (not other_migration or
migration.updated_at >= other_migration.updated_at):
filtered[uuid] = migration
for migration in filtered.values():
instance = migration.instance
instance = instances[migration.instance_uuid]
try:
self._update_usage_from_migration(context, instance, None,
migration)

View File

@ -16,6 +16,7 @@
"""Tests for compute resource tracking."""
import copy
import datetime
import six
import uuid
@ -1404,3 +1405,105 @@ class StatsInvalidTypeTestCase(BaseTrackerTestCase):
self.assertRaises(ValueError,
self.tracker.update_available_resource,
context=self.context)
class UpdateUsageFromMigrationsTestCase(BaseTrackerTestCase):
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
def test_no_migrations(self, mock_update_usage):
migrations = []
self.tracker._update_usage_from_migrations(self.context, migrations)
self.assertFalse(mock_update_usage.called)
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
def test_instance_not_found(self, mock_get_instance, mock_update_usage):
mock_get_instance.side_effect = exception.InstanceNotFound(
instance_id='some_id',
)
migration = objects.Migration(
context=self.context,
instance_uuid='some_uuid',
)
self.tracker._update_usage_from_migrations(self.context, [migration])
mock_get_instance.assert_called_once_with(self.context, 'some_uuid')
self.assertFalse(mock_update_usage.called)
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
def test_update_usage_called(self, mock_get_instance, mock_update_usage):
instance = self._fake_instance_obj()
mock_get_instance.return_value = instance
migration = objects.Migration(
context=self.context,
instance_uuid=instance.uuid,
)
self.tracker._update_usage_from_migrations(self.context, [migration])
mock_get_instance.assert_called_once_with(self.context, instance.uuid)
mock_update_usage.assert_called_once_with(
self.context, instance, None, migration)
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
def test_flavor_not_found(self, mock_get_instance, mock_update_usage):
mock_update_usage.side_effect = exception.FlavorNotFound(flavor_id='')
instance = self._fake_instance_obj()
mock_get_instance.return_value = instance
migration = objects.Migration(
context=self.context,
instance_uuid=instance.uuid,
)
self.tracker._update_usage_from_migrations(self.context, [migration])
mock_get_instance.assert_called_once_with(self.context, instance.uuid)
mock_update_usage.assert_called_once_with(
self.context, instance, None, migration)
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
def test_not_resizing_state(self, mock_get_instance, mock_update_usage):
instance = self._fake_instance_obj()
instance.vm_state = vm_states.ACTIVE
instance.task_state = task_states.SUSPENDING
mock_get_instance.return_value = instance
migration = objects.Migration(
context=self.context,
instance_uuid=instance.uuid,
)
self.tracker._update_usage_from_migrations(self.context, [migration])
mock_get_instance.assert_called_once_with(self.context, instance.uuid)
self.assertFalse(mock_update_usage.called)
@mock.patch.object(resource_tracker.ResourceTracker,
'_update_usage_from_migration')
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
def test_use_most_recent(self, mock_get_instance, mock_update_usage):
instance = self._fake_instance_obj()
mock_get_instance.return_value = instance
migration_2002 = objects.Migration(
id=2002,
context=self.context,
instance_uuid=instance.uuid,
updated_at=datetime.datetime(2002, 1, 1, 0, 0, 0),
)
migration_2003 = objects.Migration(
id=2003,
context=self.context,
instance_uuid=instance.uuid,
updated_at=datetime.datetime(2003, 1, 1, 0, 0, 0),
)
migration_2001 = objects.Migration(
id=2001,
context=self.context,
instance_uuid=instance.uuid,
updated_at=datetime.datetime(2001, 1, 1, 0, 0, 0),
)
self.tracker._update_usage_from_migrations(
self.context, [migration_2002, migration_2003, migration_2001])
mock_get_instance.assert_called_once_with(self.context, instance.uuid)
mock_update_usage.assert_called_once_with(
self.context, instance, None, migration_2003)