Remove SELECT FOR UPDATE use in ML2 type driver release_segment

SELECT FOR UPDATE expression, which is triggered with the use of the
SQLAlchemy Query object's with_lockmode('update') method, is
detrimental to performance and scalability of the database
performance code in Neutron due to the lock contention it produces.

SELECT FOR UPDATE can be entirely avoided in release_segment methods
with the use of single-shot UPDATE and DELETE expressions, and this
patch clears a number of uses of SELECT FOR UPDATE by consolidating
multiple SQL expressions into one.

Partial-Bug: #1330562
Change-Id: I29ffcafc8d4d73ac1cb50c94df5da85514d47a3f
This commit is contained in:
Cedric Brandily 2014-06-16 22:56:10 +02:00
parent 0929cccffb
commit 1814dae6b7
4 changed files with 64 additions and 69 deletions

View File

@ -118,14 +118,12 @@ class FlatTypeDriver(api.TypeDriver):
def release_segment(self, session, segment): def release_segment(self, session, segment):
physical_network = segment[api.PHYSICAL_NETWORK] physical_network = segment[api.PHYSICAL_NETWORK]
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
try: count = (session.query(FlatAllocation).
alloc = (session.query(FlatAllocation). filter_by(physical_network=physical_network).
filter_by(physical_network=physical_network). delete())
with_lockmode('update'). if count:
one()) LOG.debug("Releasing flat network on physical network %s",
session.delete(alloc) physical_network)
LOG.debug(_("Releasing flat network on physical " else:
"network %s"), physical_network) LOG.warning(_("No flat network found on physical network %s"),
except sa.orm.exc.NoResultFound: physical_network)
LOG.warning(_("No flat network found on physical network %s"),
physical_network)

View File

@ -108,24 +108,22 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
def release_segment(self, session, segment): def release_segment(self, session, segment):
gre_id = segment[api.SEGMENTATION_ID] gre_id = segment[api.SEGMENTATION_ID]
inside = any(lo <= gre_id <= hi for lo, hi in self.gre_id_ranges)
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
try: query = session.query(GreAllocation).filter_by(gre_id=gre_id)
alloc = (session.query(GreAllocation). if inside:
filter_by(gre_id=gre_id). count = query.update({"allocated": False})
with_lockmode('update'). if count:
one()) LOG.debug("Releasing gre tunnel %s to pool", gre_id)
alloc.allocated = False else:
for lo, hi in self.gre_id_ranges: count = query.delete()
if lo <= gre_id <= hi: if count:
LOG.debug(_("Releasing gre tunnel %s to pool"), LOG.debug("Releasing gre tunnel %s outside pool", gre_id)
gre_id)
break if not count:
else: LOG.warning(_("gre_id %s not found"), gre_id)
session.delete(alloc)
LOG.debug(_("Releasing gre tunnel %s outside pool"),
gre_id)
except sa_exc.NoResultFound:
LOG.warning(_("gre_id %s not found"), gre_id)
def _sync_gre_allocations(self): def _sync_gre_allocations(self):
"""Synchronize gre_allocations table with configured tunnel ranges.""" """Synchronize gre_allocations table with configured tunnel ranges."""

View File

@ -235,33 +235,31 @@ class VlanTypeDriver(api.TypeDriver):
def release_segment(self, session, segment): def release_segment(self, session, segment):
physical_network = segment[api.PHYSICAL_NETWORK] physical_network = segment[api.PHYSICAL_NETWORK]
vlan_id = segment[api.SEGMENTATION_ID] vlan_id = segment[api.SEGMENTATION_ID]
ranges = self.network_vlan_ranges.get(physical_network, [])
inside = any(lo <= vlan_id <= hi for lo, hi in ranges)
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
try: query = (session.query(VlanAllocation).
alloc = (session.query(VlanAllocation). filter_by(physical_network=physical_network,
filter_by(physical_network=physical_network, vlan_id=vlan_id))
vlan_id=vlan_id). if inside:
with_lockmode('update'). count = query.update({"allocated": False})
one()) if count:
alloc.allocated = False LOG.debug("Releasing vlan %(vlan_id)s on physical "
inside = False "network %(physical_network)s to pool",
for vlan_min, vlan_max in self.network_vlan_ranges.get(
physical_network, []):
if vlan_min <= vlan_id <= vlan_max:
inside = True
break
if not inside:
session.delete(alloc)
LOG.debug(_("Releasing vlan %(vlan_id)s on physical "
"network %(physical_network)s outside pool"),
{'vlan_id': vlan_id, {'vlan_id': vlan_id,
'physical_network': physical_network}) 'physical_network': physical_network})
else: else:
LOG.debug(_("Releasing vlan %(vlan_id)s on physical " count = query.delete()
"network %(physical_network)s to pool"), if count:
LOG.debug("Releasing vlan %(vlan_id)s on physical "
"network %(physical_network)s outside pool",
{'vlan_id': vlan_id, {'vlan_id': vlan_id,
'physical_network': physical_network}) 'physical_network': physical_network})
except sa.orm.exc.NoResultFound:
LOG.warning(_("No vlan_id %(vlan_id)s found on physical " if not count:
"network %(physical_network)s"), LOG.warning(_("No vlan_id %(vlan_id)s found on physical "
{'vlan_id': vlan_id, "network %(physical_network)s"),
'physical_network': physical_network}) {'vlan_id': vlan_id,
'physical_network': physical_network})

View File

@ -116,24 +116,25 @@ class VxlanTypeDriver(type_tunnel.TunnelTypeDriver):
def release_segment(self, session, segment): def release_segment(self, session, segment):
vxlan_vni = segment[api.SEGMENTATION_ID] vxlan_vni = segment[api.SEGMENTATION_ID]
inside = any(lo <= vxlan_vni <= hi for lo, hi in self.vxlan_vni_ranges)
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
try: query = (session.query(VxlanAllocation).
alloc = (session.query(VxlanAllocation). filter_by(vxlan_vni=vxlan_vni))
filter_by(vxlan_vni=vxlan_vni). if inside:
with_lockmode('update'). count = query.update({"allocated": False})
one()) if count:
alloc.allocated = False LOG.debug("Releasing vxlan tunnel %s to pool",
for low, high in self.vxlan_vni_ranges:
if low <= vxlan_vni <= high:
LOG.debug(_("Releasing vxlan tunnel %s to pool"),
vxlan_vni)
break
else:
session.delete(alloc)
LOG.debug(_("Releasing vxlan tunnel %s outside pool"),
vxlan_vni) vxlan_vni)
except sa_exc.NoResultFound: else:
LOG.warning(_("vxlan_vni %s not found"), vxlan_vni) count = query.delete()
if count:
LOG.debug("Releasing vxlan tunnel %s outside pool",
vxlan_vni)
if not count:
LOG.warning(_("vxlan_vni %s not found"), vxlan_vni)
def _sync_vxlan_allocations(self): def _sync_vxlan_allocations(self):
""" """