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 add_endpoint methods
with the use of single-shot SELECT and INSERT expressions and the
correction of VxlanEndpoint primary key: indeed previously it was not
possible to create multiple endpoints with the same ip, now the model
primary key constraint ensures it.
Change-Id: Id69fbc15c8f51b4b275cd742312e6ff6802d8c0f
Partial-Bug: #
1330562
--- /dev/null
+# Copyright (c) 2014 Thales Services SAS
+#
+# 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.
+#
+
+"""correct Vxlan Endpoint primary key
+
+Revision ID: 4eba2f05c2f4
+Revises: 884573acbf1c
+Create Date: 2014-07-07 22:48:38.544323
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '4eba2f05c2f4'
+down_revision = '884573acbf1c'
+
+
+from alembic import op
+
+
+TABLE_NAME = 'ml2_vxlan_endpoints'
+PK_NAME = 'ml2_vxlan_endpoints_pkey'
+
+
+def upgrade(active_plugins=None, options=None):
+ op.drop_constraint(PK_NAME, TABLE_NAME, type_='primary')
+ op.create_primary_key(PK_NAME, TABLE_NAME, cols=['ip_address'])
+
+
+def downgrade(active_plugins=None, options=None):
+ op.drop_constraint(PK_NAME, TABLE_NAME, type_='primary')
+ op.create_primary_key(PK_NAME, TABLE_NAME, cols=['ip_address', 'udp_port'])
-884573acbf1c
+4eba2f05c2f4
# under the License.
from oslo.config import cfg
+from oslo.db import exception as db_exc
from six import moves
import sqlalchemy as sa
-from sqlalchemy.orm import exc as sa_exc
from sqlalchemy import sql
from neutron.common import exceptions as exc
def add_endpoint(self, ip):
LOG.debug(_("add_gre_endpoint() called for ip %s"), ip)
session = db_api.get_session()
- with session.begin(subtransactions=True):
- try:
- gre_endpoint = (session.query(GreEndpoints).
- filter_by(ip_address=ip).one())
- LOG.warning(_("Gre endpoint with ip %s already exists"), ip)
- except sa_exc.NoResultFound:
- gre_endpoint = GreEndpoints(ip_address=ip)
- session.add(gre_endpoint)
- return gre_endpoint
+ try:
+ gre_endpoint = GreEndpoints(ip_address=ip)
+ gre_endpoint.save(session)
+ except db_exc.DBDuplicateEntry:
+ gre_endpoint = (session.query(GreEndpoints).
+ filter_by(ip_address=ip).one())
+ LOG.warning(_("Gre endpoint with ip %s already exists"), ip)
+ return gre_endpoint
# @author: Kyle Mestery, Cisco Systems, Inc.
from oslo.config import cfg
+from oslo.db import exception as db_exc
import sqlalchemy as sa
-from sqlalchemy.orm import exc as sa_exc
from sqlalchemy import sql
from neutron.common import exceptions as exc
__tablename__ = 'ml2_vxlan_endpoints'
ip_address = sa.Column(sa.String(64), primary_key=True)
- udp_port = sa.Column(sa.Integer, primary_key=True, nullable=False,
- autoincrement=False)
+ udp_port = sa.Column(sa.Integer, nullable=False)
def __repr__(self):
return "<VxlanTunnelEndpoint(%s)>" % self.ip_address
def add_endpoint(self, ip, udp_port=VXLAN_UDP_PORT):
LOG.debug(_("add_vxlan_endpoint() called for ip %s"), ip)
session = db_api.get_session()
- with session.begin(subtransactions=True):
- try:
- vxlan_endpoint = (session.query(VxlanEndpoints).
- filter_by(ip_address=ip).
- with_lockmode('update').one())
- except sa_exc.NoResultFound:
- vxlan_endpoint = VxlanEndpoints(ip_address=ip,
- udp_port=udp_port)
- session.add(vxlan_endpoint)
- return vxlan_endpoint
+ try:
+ vxlan_endpoint = VxlanEndpoints(ip_address=ip,
+ udp_port=udp_port)
+ vxlan_endpoint.save(session)
+ except db_exc.DBDuplicateEntry:
+ vxlan_endpoint = (session.query(VxlanEndpoints).
+ filter_by(ip_address=ip).one())
+ LOG.warning(_("Vxlan endpoint with ip %s already exists"), ip)
+ return vxlan_endpoint
# License for the specific language governing permissions and limitations
# under the License.
+import mock
from six import moves
import testtools
from testtools import matchers
self.assertIn(endpoint['ip_address'],
[TUNNEL_IP_ONE, TUNNEL_IP_TWO])
+ def test_add_same_endpoints(self):
+ self.driver.add_endpoint(TUNNEL_IP_ONE)
+ with mock.patch.object(type_gre.LOG, 'warning') as log_warn:
+ self.driver.add_endpoint(TUNNEL_IP_ONE)
+ log_warn.assert_called_once_with(mock.ANY, TUNNEL_IP_ONE)
+
class GreTypeMultiRangeTest(base.BaseTestCase):
# under the License.
# @author: Kyle Mestery, Cisco Systems, Inc.
+import mock
from oslo.config import cfg
from six import moves
import testtools
elif endpoint['ip_address'] == TUNNEL_IP_TWO:
self.assertEqual(VXLAN_UDP_PORT_TWO, endpoint['udp_port'])
+ def test_add_same_ip_endpoints(self):
+ self.driver.add_endpoint(TUNNEL_IP_ONE, VXLAN_UDP_PORT_ONE)
+ with mock.patch.object(type_vxlan.LOG, 'warning') as log_warn:
+ observed = self.driver.add_endpoint(TUNNEL_IP_ONE,
+ VXLAN_UDP_PORT_TWO)
+ self.assertEqual(VXLAN_UDP_PORT_ONE, observed['udp_port'])
+ log_warn.assert_called_once_with(mock.ANY, TUNNEL_IP_ONE)
+
class VxlanTypeMultiRangeTest(base.BaseTestCase):