]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Change transaction isolation so retry logic could work properly
authorEugene Nikanorov <enikanorov@mirantis.com>
Mon, 17 Nov 2014 07:00:49 +0000 (11:00 +0400)
committerenikanorov <enikanorov@mirantis.com>
Fri, 16 Jan 2015 09:08:09 +0000 (09:08 +0000)
Lower isolation level from REPEATABLE READ to READ COMMITTED for
transaction that is used to create a network.
This allows retry logic to see changes done in other connections
while doing the same query.
Perform that only for mysql db backend.

Change-Id: I6b9d9212c37fe028566e0df4a3dfa51f284ce6e9
Closes-Bug: #1382064

neutron/db/sqlalchemyutils.py
neutron/plugins/ml2/plugin.py
neutron/tests/functional/db/test_sqla_utils.py [new file with mode: 0644]

index b21edc83e442b532728cfafd8218edf295242eae..19cc618871123132fb699e429af6f5201ba274e7 100644 (file)
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import contextlib
+
 from six import moves
 import sqlalchemy
 from sqlalchemy.orm import properties
@@ -105,3 +107,27 @@ def paginate_query(query, model, limit, sorts, marker_obj=None):
         query = query.limit(limit)
 
     return query
+
+
+default_tx_isolation_level = None
+
+
+def get_default_tx_level(engine):
+    global default_tx_isolation_level
+    if not default_tx_isolation_level:
+        default_tx_isolation_level = engine.dialect.get_isolation_level(
+            engine.raw_connection())
+    return default_tx_isolation_level
+
+
+@contextlib.contextmanager
+def set_mysql_tx_isolation_level(session, level):
+    engine = session.connection().engine
+    if (engine.name == "mysql" and level != get_default_tx_level(engine)):
+        session.connection().execution_options(
+            isolation_level=level)
+    yield
+    engine = session.connection().engine
+    if engine.name == "mysql":
+        session.connection().execution_options(
+            isolation_level=get_default_tx_level(engine))
index 3a68f204718cf8bc42d81f781d38b99b5c4e2b7e..96868018e8cc8a25aced9e12d026ed7731f3a67a 100644 (file)
@@ -48,6 +48,7 @@ from neutron.db import extradhcpopt_db
 from neutron.db import models_v2
 from neutron.db import quota_db  # noqa
 from neutron.db import securitygroups_rpc_base as sg_db_rpc
+from neutron.db import sqlalchemyutils as sqla
 from neutron.extensions import allowedaddresspairs as addr_pair
 from neutron.extensions import extra_dhcp_opt as edo_ext
 from neutron.extensions import l3agentscheduler
@@ -538,7 +539,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
         net_data = network[attributes.NETWORK]
         tenant_id = self._get_tenant_id_for_create(context, net_data)
         session = context.session
-        with session.begin(subtransactions=True):
+        with contextlib.nested(
+            session.begin(subtransactions=True),
+            sqla.set_mysql_tx_isolation_level(session, "READ COMMITTED")
+        ):
             self._ensure_default_security_group(context, tenant_id)
             result = super(Ml2Plugin, self).create_network(context, network)
             self.extension_manager.process_create_network(session, net_data,
diff --git a/neutron/tests/functional/db/test_sqla_utils.py b/neutron/tests/functional/db/test_sqla_utils.py
new file mode 100644 (file)
index 0000000..96c0c94
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright (c) 2014 OpenStack Foundation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import contextlib
+
+from oslo.config import cfg
+from oslo.db.sqlalchemy import test_base
+
+from neutron import context
+from neutron.db import sqlalchemyutils
+from neutron.tests import base
+
+
+class TestSettingTXIsolationLevel(base.BaseTestCase,
+                                  test_base.MySQLOpportunisticTestCase):
+    """Check that transaction isolation level indeed changes."""
+
+    def setUp(self):
+        super(TestSettingTXIsolationLevel, self).setUp()
+        cfg.CONF.set_override('connection',
+                              self.engine.url,
+                              group='database')
+
+    def _get_session_tx_isolation(self, session):
+        sql = "SELECT @@tx_isolation;"
+        res = session.connection().execute(sql)
+        res = [r for r in res]
+        res = [r for r in res[0]]
+        return res[0]
+
+    def test_set_tx_iso_level_changes_back_and_forth_mysql(self):
+        ctx = context.get_admin_context()
+        default_level = sqlalchemyutils.get_default_tx_level(self.engine)
+        other_level = ("READ COMMITTED" if default_level == "REPEATABLE READ"
+                       else "REPEATABLE READ")
+        with contextlib.nested(
+            ctx.session.begin(subtransactions=True),
+            sqlalchemyutils.set_mysql_tx_isolation_level(
+                ctx.session, other_level)
+        ):
+            res = self._get_session_tx_isolation(ctx.session)
+            self.assertEqual(other_level.replace(' ', '-'), res)
+        #check that context manager changes tx isolation level back
+        res = self._get_session_tx_isolation(ctx.session)
+        self.assertEqual(default_level.replace(' ', '-'), res)