Wrap ML2 delete_port with db retry decorator

ML2 delete_port operation currently involves locking ports
and bindings tables which may lead to DBDeadlock errors in certain
cases when several ports are deleted concurrently.
That may happen due to specifics of Galera working in active-active
mode: it may throw deadlock errors when it fails to validate
a change with other members of the cluster.
The fix adds retries to delete port operation to overcome such
deadlocks

Conflicts:
    neutron/plugins/ml2/plugin.py
    neutron/tests/unit/ml2/test_ml2_plugin.py

Closes-Bug: #1422504
Change-Id: I684691d59c5ac370d74314c3c91857dc709b2d9b
(cherry picked from commit 45ea2cf100)
(cherry picked from commit 151b28cf1c)
This commit is contained in:
Oleg Bondarev 2015-05-06 12:50:11 +03:00 committed by Jakub Libosvar
parent 169cbd9164
commit f0bb982bfc
2 changed files with 19 additions and 0 deletions

View File

@ -991,6 +991,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
cur_binding.driver = new_binding.driver
cur_binding.segment = new_binding.segment
@db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
retry_on_deadlock=True)
def delete_port(self, context, id, l3_port_check=True):
LOG.debug(_("Deleting port %s"), id)
removed_routers = []

View File

@ -19,6 +19,8 @@ import testtools
import uuid
import webob
from oslo.db import exception as db_exc
from neutron.common import constants
from neutron.common import exceptions as exc
from neutron.common import utils
@ -239,6 +241,21 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
# by the called method
self.assertIsNone(l3plugin.disassociate_floatingips(ctx, port_id))
def test_delete_port_tolerates_db_deadlock(self):
ctx = context.get_admin_context()
plugin = manager.NeutronManager.get_plugin()
with self.port() as port:
port_db, binding = ml2_db.get_locked_port_and_binding(
ctx.session, port['port']['id'])
with mock.patch('neutron.plugins.ml2.plugin.'
'db.get_locked_port_and_binding') as lock:
lock.side_effect = [db_exc.DBDeadlock,
(port_db, binding)]
plugin.delete_port(ctx, port['port']['id'])
self.assertEqual(2, lock.call_count)
self.assertRaises(
exc.PortNotFound, plugin.get_port, ctx, port['port']['id'])
class TestMl2DvrPortsV2(TestMl2PortsV2):
def setUp(self):