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

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

View File

@ -1213,6 +1213,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
raise e.errors[0].error
raise exc.ServicePortInUse(port_id=port_id, reason=e)
@oslo_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):
self._pre_delete_port(context, id, l3_port_check)
# TODO(armax): get rid of the l3 dependency in the with block

View File

@ -498,6 +498,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 TestMl2PluginOnly(Ml2PluginV2TestCase):
"""For testing methods that don't call drivers"""