Make revision bump robust to concurrent removals
If there expired objects in the session that are concurrently deleted by another sesssion, the revision code will get an ObjectDeletedError when it goes to check if the expired object is a related object to the object it is handling. So we need to catch this error an continue on since there is nothing it needs to do. Change-Id: Ie6034f36c34099f2646840cbdd569a4398534df5 Closes-Bug: #1611627
This commit is contained in:
parent
f80bd6d121
commit
9e8bcf45a5
|
@ -13,6 +13,7 @@
|
|||
|
||||
from oslo_log import log as logging
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy.orm import exc
|
||||
from sqlalchemy.orm import session as se
|
||||
|
||||
from neutron._i18n import _, _LW
|
||||
|
@ -53,17 +54,21 @@ class RevisionPlugin(service_base.ServicePluginBase):
|
|||
|
||||
def _bump_related_revisions(self, session, obj):
|
||||
for revises_col in getattr(obj, 'revises_on_change', ()):
|
||||
related_obj = self._find_related_obj(session, obj, revises_col)
|
||||
if not related_obj:
|
||||
LOG.warning(_LW("Could not find related %(col)s for resource "
|
||||
"%(obj)s to bump revision."),
|
||||
{'obj': obj, 'col': revises_col})
|
||||
continue
|
||||
# if related object revises others, bump those as well
|
||||
self._bump_related_revisions(session, related_obj)
|
||||
# no need to bump revisions on related objects being deleted
|
||||
if related_obj not in session.deleted:
|
||||
related_obj.bump_revision()
|
||||
try:
|
||||
related_obj = self._find_related_obj(session, obj, revises_col)
|
||||
if not related_obj:
|
||||
LOG.warning(_LW("Could not find related %(col)s for "
|
||||
"resource %(obj)s to bump revision."),
|
||||
{'obj': obj, 'col': revises_col})
|
||||
continue
|
||||
# if related object revises others, bump those as well
|
||||
self._bump_related_revisions(session, related_obj)
|
||||
# no need to bump revisions on related objects being deleted
|
||||
if related_obj not in session.deleted:
|
||||
related_obj.bump_revision()
|
||||
except exc.ObjectDeletedError:
|
||||
# object was in session but another writer deleted it
|
||||
pass
|
||||
|
||||
def get_plugin_type(self):
|
||||
return "revision_plugin"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import netaddr
|
||||
|
||||
from neutron import context as nctx
|
||||
from neutron.db import models_v2
|
||||
from neutron import manager
|
||||
from neutron.tests.unit.plugins.ml2 import test_plugin
|
||||
|
||||
|
@ -34,6 +35,20 @@ class TestRevisionPlugin(test_plugin.Ml2PluginV2TestCase):
|
|||
get_service_plugins()['L3_ROUTER_NAT'])
|
||||
self.ctx = nctx.get_admin_context()
|
||||
|
||||
def test_handle_expired_object(self):
|
||||
rp = manager.NeutronManager.get_service_plugins()['revision_plugin']
|
||||
with self.port():
|
||||
with self.ctx.session.begin():
|
||||
ipal_obj = self.ctx.session.query(models_v2.IPAllocation).one()
|
||||
# load port into our session
|
||||
port_obj = self.ctx.session.query(models_v2.Port).one()
|
||||
# simulate concurrent delete in another session
|
||||
nctx.get_admin_context().session.query(models_v2.Port).delete()
|
||||
# expire the port so the revision bumping code will trigger a
|
||||
# lookup on its attributes and encounter an ObjectDeletedError
|
||||
self.ctx.session.expire(port_obj)
|
||||
rp._bump_related_revisions(self.ctx.session, ipal_obj)
|
||||
|
||||
def test_port_name_update_revises(self):
|
||||
with self.port() as port:
|
||||
rev = port['port']['revision']
|
||||
|
|
Loading…
Reference in New Issue