Manually increment revision numbers in revision plugin

Don't rely on the SQLAlchemy revision_col flag to bump revision
numbers and instead bump them in a before_flush handler. This
will allow the follow-up patch to do enforcement on conditional
updates before the revision number is incremented.

Partial-Bug: #1493714
Partially-Implements: blueprint push-notifications
Change-Id: I5feeec5b8385727eff53dc669363bc41db8ceaba
This commit is contained in:
Kevin Benton 2017-06-14 15:36:55 -07:00
parent 72ac25c929
commit 9c0a0de556
3 changed files with 28 additions and 6 deletions

View File

@ -66,7 +66,8 @@ class StandardAttribute(model_base.BASEV2):
__mapper_args__ = {
# see http://docs.sqlalchemy.org/en/latest/orm/versioning.html for
# details about how this works
"version_id_col": revision_number
"version_id_col": revision_number,
"version_id_generator": False # revision plugin increments manually
}
def bump_revision(self):

View File

@ -34,12 +34,16 @@ class RevisionPlugin(service_base.ServicePluginBase):
def __init__(self):
super(RevisionPlugin, self).__init__()
db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions)
db_api.sqla_listen(se.Session, 'after_commit',
self._clear_rev_bumped_flags)
db_api.sqla_listen(se.Session, 'after_rollback',
self._clear_rev_bumped_flags)
def bump_revisions(self, session, context, instances):
# bump revision number for any updated objects in the session
for obj in session.dirty:
if isinstance(obj, standard_attr.HasStandardAttributes):
obj.bump_revision()
self._bump_obj_revision(session, obj)
# see if any created/updated/deleted objects bump the revision
# of another object
@ -63,7 +67,7 @@ class RevisionPlugin(service_base.ServicePluginBase):
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()
self._bump_obj_revision(session, related_obj)
except exc.ObjectDeletedError:
# object was in session but another writer deleted it
pass
@ -98,3 +102,20 @@ class RevisionPlugin(service_base.ServicePluginBase):
"have load_on_pending set to True to "
"bump parent revisions on create: %s"),
relationship_col)
def _clear_rev_bumped_flags(self, session):
"""This clears all flags on commit/rollback to enable rev bumps."""
for inst in session:
setattr(inst, '_rev_bumped', False)
def _bump_obj_revision(self, session, obj):
"""Increment object revision in compare and swap fashion.
Before the increment, this checks and enforces any revision number
constraints.
"""
if getattr(obj, '_rev_bumped', False):
# we've already bumped the revision of this object in this txn
return
obj.bump_revision()
setattr(obj, '_rev_bumped', True)

View File

@ -108,18 +108,18 @@ class TrunkTestJSON(TrunkTestJSONBase):
@decorators.idempotent_id('4ce46c22-a2b6-4659-bc5a-0ef2463cab32')
def test_create_update_trunk(self):
trunk = self._create_trunk_with_network_and_parent(None)
self.assertEqual(1, trunk['trunk']['revision_number'])
rev = trunk['trunk']['revision_number']
trunk_id = trunk['trunk']['id']
res = self._show_trunk(trunk_id)
self.assertTrue(res['trunk']['admin_state_up'])
self.assertEqual(1, res['trunk']['revision_number'])
self.assertEqual(rev, res['trunk']['revision_number'])
self.assertEqual("", res['trunk']['name'])
self.assertEqual("", res['trunk']['description'])
res = self.client.update_trunk(
trunk_id, name='foo', admin_state_up=False)
self.assertFalse(res['trunk']['admin_state_up'])
self.assertEqual("foo", res['trunk']['name'])
self.assertGreater(res['trunk']['revision_number'], 1)
self.assertGreater(res['trunk']['revision_number'], rev)
# enable the trunk so that it can be managed
self.client.update_trunk(trunk_id, admin_state_up=True)