]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Improve performance of security group DB query
authorKevin Benton <blak111@gmail.com>
Wed, 24 Sep 2014 12:23:32 +0000 (05:23 -0700)
committerKevin Benton <blak111@gmail.com>
Sat, 18 Oct 2014 02:59:02 +0000 (19:59 -0700)
The _select_ips_for_remote_group method was joining the
IP allocation, port, allowed address pair, and security group tables
together in a single query. Additionally, it was loading all of
the port columns and using none of them. This resulted in a
very expensive query with no benefit.

This patch eliminates the unnecessary use of the port table by joining
the IP allocation table directly to the security groups and allowed
address pairs tables. In local testing of the method, this sped it up
by an order of magnitude.

Closes-Bug: #1373851
Change-Id: I12899413004838d2d22b691f1e2f3b18f7ec2c27

neutron/db/securitygroups_rpc_base.py

index e73b2f6142e498cc28d693bc680108b9d7e95ed9..1b848afc18226c1fdd13dd1ed5289a8693e15447 100644 (file)
@@ -19,6 +19,7 @@ from sqlalchemy.orm import exc
 from neutron.common import constants as q_const
 from neutron.common import ipv6_utils as ipv6
 from neutron.common import utils
+from neutron.db import allowedaddresspairs_db as addr_pair
 from neutron.db import models_v2
 from neutron.db import securitygroups_db as sg_db
 from neutron.extensions import securitygroup as ext_sg
@@ -231,27 +232,32 @@ class SecurityGroupServerRpcMixin(sg_db.SecurityGroupDbMixin):
         if not remote_group_ids:
             return ips_by_group
         for remote_group_id in remote_group_ids:
-            ips_by_group[remote_group_id] = []
+            ips_by_group[remote_group_id] = set()
 
         ip_port = models_v2.IPAllocation.port_id
         sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
         sg_binding_sgid = sg_db.SecurityGroupPortBinding.security_group_id
 
+        # Join the security group binding table directly to the IP allocation
+        # table instead of via the Port table skip an unnecessary intermediary
         query = context.session.query(sg_binding_sgid,
-                                      models_v2.Port,
-                                      models_v2.IPAllocation.ip_address)
+                                      models_v2.IPAllocation.ip_address,
+                                      addr_pair.AllowedAddressPair.ip_address)
         query = query.join(models_v2.IPAllocation,
                            ip_port == sg_binding_port)
-        query = query.join(models_v2.Port,
-                           ip_port == models_v2.Port.id)
+        # Outerjoin because address pairs may be null and we still want the
+        # IP for the port.
+        query = query.outerjoin(
+            addr_pair.AllowedAddressPair,
+            sg_binding_port == addr_pair.AllowedAddressPair.port_id)
         query = query.filter(sg_binding_sgid.in_(remote_group_ids))
-        for security_group_id, port, ip_address in query:
-            ips_by_group[security_group_id].append(ip_address)
-            # if there are allowed_address_pairs add them
-            if getattr(port, 'allowed_address_pairs', None):
-                for address_pair in port.allowed_address_pairs:
-                    ips_by_group[security_group_id].append(
-                        address_pair['ip_address'])
+        # Each allowed address pair IP record for a port beyond the 1st
+        # will have a duplicate regular IP in the query response since
+        # the relationship is 1-to-many. Dedup with a set
+        for security_group_id, ip_address, allowed_addr_ip in query:
+            ips_by_group[security_group_id].add(ip_address)
+            if allowed_addr_ip:
+                ips_by_group[security_group_id].add(allowed_addr_ip)
         return ips_by_group
 
     def _select_remote_group_ids(self, ports):