Check DBReferenceError foreign key in Instance.save
In commit 5f869d0c04 handling of
DBReferenceError from oslo.db was added to raise InstanceNotFound.
However, if the target row has foreign key constraints to other
tables, the DBReferenceError raised doesn't necessarily imply that
the instance row is missing.
This change adds a check on the key attribute in the DBReferenceError
exception object and raises InstanceNotFound only if the key is
'instance_uuid' -- otherwise, it reraises DBReferenceError.
Related-Bug: #1462128
Related-Bug: #1497076
Change-Id: I131dbc27639626e89735ac0591dc266f35e6ac80
This commit is contained in:
@@ -615,7 +615,9 @@ class _BaseInstance(base.NovaPersistentObject, base.NovaObject,
|
||||
except AttributeError:
|
||||
LOG.exception(_LE('No save handler for %s'), field,
|
||||
instance=self)
|
||||
except db_exc.DBReferenceError:
|
||||
except db_exc.DBReferenceError as exp:
|
||||
if exp.key != 'instance_uuid':
|
||||
raise
|
||||
# NOTE(melwitt): This will happen if we instance.save()
|
||||
# before an instance.create() and FK constraint fails.
|
||||
# In practice, this occurs in cells during a delete of
|
||||
|
||||
@@ -1262,12 +1262,13 @@ class _TestInstanceObject(object):
|
||||
|
||||
class TestInstanceObject(test_objects._LocalTest,
|
||||
_TestInstanceObject):
|
||||
def test_save_objectfield_missing_instance_row(self):
|
||||
def _test_save_objectfield_fk_constraint_fails(self, foreign_key,
|
||||
expected_exception):
|
||||
# NOTE(danms): Do this here and not in the remote test because
|
||||
# we're mocking out obj_attr_is_set() without the thing actually
|
||||
# being set, which confuses the heck out of the serialization
|
||||
# stuff.
|
||||
error = db_exc.DBReferenceError('table', 'constraint', 'key',
|
||||
error = db_exc.DBReferenceError('table', 'constraint', foreign_key,
|
||||
'key_table')
|
||||
# Prevent lazy-loading any fields, results in InstanceNotFound
|
||||
attrs = objects.instance.INSTANCE_OPTIONAL_ATTRS
|
||||
@@ -1283,11 +1284,18 @@ class TestInstanceObject(test_objects._LocalTest,
|
||||
mock_save_field.side_effect = error
|
||||
instance.obj_reset_changes(fields=[field])
|
||||
instance._changed_fields.add(field)
|
||||
self.assertRaises(exception.InstanceNotFound,
|
||||
instance.save)
|
||||
self.assertRaises(expected_exception, instance.save)
|
||||
instance.obj_reset_changes(fields=[field])
|
||||
_test()
|
||||
|
||||
def test_save_objectfield_missing_instance_row(self):
|
||||
self._test_save_objectfield_fk_constraint_fails(
|
||||
'instance_uuid', exception.InstanceNotFound)
|
||||
|
||||
def test_save_objectfield_reraises_if_not_instance_related(self):
|
||||
self._test_save_objectfield_fk_constraint_fails(
|
||||
'other_foreign_key', db_exc.DBReferenceError)
|
||||
|
||||
|
||||
class TestRemoteInstanceObject(test_objects._RemoteTest,
|
||||
_TestInstanceObject):
|
||||
|
||||
Reference in New Issue
Block a user