summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Benton <kevin@benton.pub>2017-06-14 15:36:55 -0700
committerKevin Benton <kevin@benton.pub>2017-06-14 18:47:17 -0700
commit9c0a0de556f8edafd153a7d4c1c0548dd4ff3129 (patch)
tree40c0fa486422ab04651082bc9f1848084ed88f58
parent72ac25c9298dd73e144e7e8d5e7dff993590ca85 (diff)
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
Notes
Notes (review): Code-Review+2: Ihar Hrachyshka <ihrachys@redhat.com> Code-Review+1: Miguel Lavalle <malavall@us.ibm.com> Code-Review+2: garyk <gkotton@vmware.com> Workflow+1: garyk <gkotton@vmware.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Sun, 25 Jun 2017 13:37:49 +0000 Reviewed-on: https://review.openstack.org/474383 Project: openstack/neutron Branch: refs/heads/master
-rw-r--r--neutron/db/standard_attr.py3
-rw-r--r--neutron/services/revisions/revision_plugin.py25
-rw-r--r--neutron/tests/tempest/api/test_trunk.py6
3 files changed, 28 insertions, 6 deletions
diff --git a/neutron/db/standard_attr.py b/neutron/db/standard_attr.py
index e51eb5a..a6e1c6f 100644
--- a/neutron/db/standard_attr.py
+++ b/neutron/db/standard_attr.py
@@ -66,7 +66,8 @@ class StandardAttribute(model_base.BASEV2):
66 __mapper_args__ = { 66 __mapper_args__ = {
67 # see http://docs.sqlalchemy.org/en/latest/orm/versioning.html for 67 # see http://docs.sqlalchemy.org/en/latest/orm/versioning.html for
68 # details about how this works 68 # details about how this works
69 "version_id_col": revision_number 69 "version_id_col": revision_number,
70 "version_id_generator": False # revision plugin increments manually
70 } 71 }
71 72
72 def bump_revision(self): 73 def bump_revision(self):
diff --git a/neutron/services/revisions/revision_plugin.py b/neutron/services/revisions/revision_plugin.py
index 4aa9fa3..ef48689 100644
--- a/neutron/services/revisions/revision_plugin.py
+++ b/neutron/services/revisions/revision_plugin.py
@@ -34,12 +34,16 @@ class RevisionPlugin(service_base.ServicePluginBase):
34 def __init__(self): 34 def __init__(self):
35 super(RevisionPlugin, self).__init__() 35 super(RevisionPlugin, self).__init__()
36 db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions) 36 db_api.sqla_listen(se.Session, 'before_flush', self.bump_revisions)
37 db_api.sqla_listen(se.Session, 'after_commit',
38 self._clear_rev_bumped_flags)
39 db_api.sqla_listen(se.Session, 'after_rollback',
40 self._clear_rev_bumped_flags)
37 41
38 def bump_revisions(self, session, context, instances): 42 def bump_revisions(self, session, context, instances):
39 # bump revision number for any updated objects in the session 43 # bump revision number for any updated objects in the session
40 for obj in session.dirty: 44 for obj in session.dirty:
41 if isinstance(obj, standard_attr.HasStandardAttributes): 45 if isinstance(obj, standard_attr.HasStandardAttributes):
42 obj.bump_revision() 46 self._bump_obj_revision(session, obj)
43 47
44 # see if any created/updated/deleted objects bump the revision 48 # see if any created/updated/deleted objects bump the revision
45 # of another object 49 # of another object
@@ -63,7 +67,7 @@ class RevisionPlugin(service_base.ServicePluginBase):
63 self._bump_related_revisions(session, related_obj) 67 self._bump_related_revisions(session, related_obj)
64 # no need to bump revisions on related objects being deleted 68 # no need to bump revisions on related objects being deleted
65 if related_obj not in session.deleted: 69 if related_obj not in session.deleted:
66 related_obj.bump_revision() 70 self._bump_obj_revision(session, related_obj)
67 except exc.ObjectDeletedError: 71 except exc.ObjectDeletedError:
68 # object was in session but another writer deleted it 72 # object was in session but another writer deleted it
69 pass 73 pass
@@ -98,3 +102,20 @@ class RevisionPlugin(service_base.ServicePluginBase):
98 "have load_on_pending set to True to " 102 "have load_on_pending set to True to "
99 "bump parent revisions on create: %s"), 103 "bump parent revisions on create: %s"),
100 relationship_col) 104 relationship_col)
105
106 def _clear_rev_bumped_flags(self, session):
107 """This clears all flags on commit/rollback to enable rev bumps."""
108 for inst in session:
109 setattr(inst, '_rev_bumped', False)
110
111 def _bump_obj_revision(self, session, obj):
112 """Increment object revision in compare and swap fashion.
113
114 Before the increment, this checks and enforces any revision number
115 constraints.
116 """
117 if getattr(obj, '_rev_bumped', False):
118 # we've already bumped the revision of this object in this txn
119 return
120 obj.bump_revision()
121 setattr(obj, '_rev_bumped', True)
diff --git a/neutron/tests/tempest/api/test_trunk.py b/neutron/tests/tempest/api/test_trunk.py
index 3e035a0..aef82e7 100644
--- a/neutron/tests/tempest/api/test_trunk.py
+++ b/neutron/tests/tempest/api/test_trunk.py
@@ -108,18 +108,18 @@ class TrunkTestJSON(TrunkTestJSONBase):
108 @decorators.idempotent_id('4ce46c22-a2b6-4659-bc5a-0ef2463cab32') 108 @decorators.idempotent_id('4ce46c22-a2b6-4659-bc5a-0ef2463cab32')
109 def test_create_update_trunk(self): 109 def test_create_update_trunk(self):
110 trunk = self._create_trunk_with_network_and_parent(None) 110 trunk = self._create_trunk_with_network_and_parent(None)
111 self.assertEqual(1, trunk['trunk']['revision_number']) 111 rev = trunk['trunk']['revision_number']
112 trunk_id = trunk['trunk']['id'] 112 trunk_id = trunk['trunk']['id']
113 res = self._show_trunk(trunk_id) 113 res = self._show_trunk(trunk_id)
114 self.assertTrue(res['trunk']['admin_state_up']) 114 self.assertTrue(res['trunk']['admin_state_up'])
115 self.assertEqual(1, res['trunk']['revision_number']) 115 self.assertEqual(rev, res['trunk']['revision_number'])
116 self.assertEqual("", res['trunk']['name']) 116 self.assertEqual("", res['trunk']['name'])
117 self.assertEqual("", res['trunk']['description']) 117 self.assertEqual("", res['trunk']['description'])
118 res = self.client.update_trunk( 118 res = self.client.update_trunk(
119 trunk_id, name='foo', admin_state_up=False) 119 trunk_id, name='foo', admin_state_up=False)
120 self.assertFalse(res['trunk']['admin_state_up']) 120 self.assertFalse(res['trunk']['admin_state_up'])
121 self.assertEqual("foo", res['trunk']['name']) 121 self.assertEqual("foo", res['trunk']['name'])
122 self.assertGreater(res['trunk']['revision_number'], 1) 122 self.assertGreater(res['trunk']['revision_number'], rev)
123 # enable the trunk so that it can be managed 123 # enable the trunk so that it can be managed
124 self.client.update_trunk(trunk_id, admin_state_up=True) 124 self.client.update_trunk(trunk_id, admin_state_up=True)
125 125