From a5dd5456bf6396abc9dd223cc060cba9da8578c8 Mon Sep 17 00:00:00 2001 From: Aaron Chung Date: Mon, 4 May 2026 22:26:54 -0700 Subject: [PATCH 1/2] Batch NIC and VLAN reads in collectVmNetworkStatistics to reduce per-NIC DB round-trips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit collectVmNetworkStatistics performed per-NIC: search by MAC, listVlansByNetworkId, producing 2 * NIC_COUNT queries per VM. Now batch-loads all NICs by MAC addresses and all VLANs by network IDs upfront (2 queries total for the read side). - Add NicDao.listByMacAddresses(List) for batch MAC lookup - Add VlanDao.listVlansByNetworkIds(List) for batch network VLAN lookup - Replace per-iteration NIC search + VLAN lookup with map lookups - Write side (lock + update) intentionally unchanged — row-level lock is load-bearing --- .../main/java/com/cloud/dc/dao/VlanDao.java | 2 ++ .../java/com/cloud/dc/dao/VlanDaoImpl.java | 10 ++++++ .../main/java/com/cloud/vm/dao/NicDao.java | 2 ++ .../java/com/cloud/vm/dao/NicDaoImpl.java | 10 ++++++ .../java/com/cloud/vm/UserVmManagerImpl.java | 32 ++++++++++++++++--- 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java index a6c267bb189b..beab7ce2cd56 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java @@ -51,6 +51,8 @@ public interface VlanDao extends GenericDao { List listVlansByNetworkId(long networkId); + List listVlansByNetworkIds(List networkIds); + List listVlansByNetworkIdIncludingRemoved(long networkId); List listVlansByPhysicalNetworkId(long physicalNetworkId); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index d9fad3cad12a..96e828bbfcb6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -392,6 +392,16 @@ public List listVlansByNetworkId(long networkId) { return listBy(sc); } + @Override + public List listVlansByNetworkIds(List networkIds) { + SearchBuilder sb = createSearchBuilder(); + sb.and("networkId", sb.entity().getNetworkId(), SearchCriteria.Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("networkId", networkIds.toArray()); + return listBy(sc); + } + @Override public List listVlansByNetworkIdIncludingRemoved(long networkId) { SearchCriteria sc = NetworkVlanSearch.create(); sc.setParameters("networkId", networkId); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java index 96a1b6e3bd16..2718127d648a 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java @@ -97,6 +97,8 @@ public interface NicDao extends GenericDao { NicVO findByMacAddress(String macAddress, long networkId); + List listByMacAddresses(List macAddresses); + NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac); List findNicsByIpv6GatewayIpv6CidrAndReserver(String ipv6Gateway, String ipv6Cidr, String reserverName); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java index 78966a09e97c..48c13ff2c5b7 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -427,6 +427,16 @@ public NicVO findByMacAddress(String macAddress, long networkId) { return findOneBy(sc); } + @Override + public List listByMacAddresses(List macAddresses) { + SearchBuilder sb = createSearchBuilder(); + sb.and("macAddress", sb.entity().getMacAddress(), Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("macAddress", macAddresses.toArray()); + return listBy(sc); + } + @Override public List findNicsByIpv6GatewayIpv6CidrAndReserver(String ipv6Gateway, String ipv6Cidr, String reserverName) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 83cce4351b24..c89fec7814dc 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5195,11 +5195,35 @@ public void doInTransactionWithoutResult(TransactionStatus status) { return; } + List macAddresses = new ArrayList<>(vmNetworkStats.size()); + for (VmNetworkStatsEntry entry : vmNetworkStats) { + macAddresses.add(entry.getMacAddress()); + } + Map nicsByMac = new HashMap<>(); + if (!macAddresses.isEmpty()) { + for (NicVO nic : _nicDao.listByMacAddresses(macAddresses)) { + nicsByMac.put(nic.getMacAddress(), nic); + } + } + + Set networkIds = new HashSet<>(); + for (NicVO nic : nicsByMac.values()) { + networkIds.add(nic.getNetworkId()); + } + Map> vlansByNetwork = new HashMap<>(); + if (!networkIds.isEmpty()) { + for (VlanVO vlan : _vlanDao.listVlansByNetworkIds(new ArrayList<>(networkIds))) { + vlansByNetwork.computeIfAbsent(vlan.getNetworkId(), k -> new ArrayList<>()).add(vlan); + } + } + for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) { - SearchCriteria sc_nic = _nicDao.createSearchCriteria(); - sc_nic.addAnd("macAddress", SearchCriteria.Op.EQ, vmNetworkStat.getMacAddress()); - NicVO nic = _nicDao.search(sc_nic, null).get(0); - List vlan = _vlanDao.listVlansByNetworkId(nic.getNetworkId()); + NicVO nic = nicsByMac.get(vmNetworkStat.getMacAddress()); + if (nic == null) { + logger.warn("Unable to find nic for mac " + vmNetworkStat.getMacAddress()); + continue; + } + List vlan = vlansByNetwork.get(nic.getNetworkId()); if (vlan == null || vlan.size() == 0 || vlan.get(0).getVlanType() != VlanType.DirectAttached) { break; // only get network statistics for DirectAttached network (shared networks in Basic zone and Advanced zone with/without SG) From 03e3ab20ad9abea69797e60480250518a38a4f50 Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Tue, 23 Jun 2026 17:10:28 +0530 Subject: [PATCH 2/2] Reuse cached search builders with IN operator and guard empty lists in batch NIC/VLAN reads --- .../src/main/java/com/cloud/dc/dao/VlanDaoImpl.java | 12 +++++++----- .../src/main/java/com/cloud/vm/dao/NicDaoImpl.java | 13 +++++++------ .../main/java/com/cloud/vm/UserVmManagerImpl.java | 12 ++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index 96e828bbfcb6..5687e8588772 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -27,6 +28,7 @@ import javax.naming.ConfigurationException; import com.cloud.dc.VlanDetailsVO; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -134,7 +136,7 @@ public VlanDaoImpl() { ZoneTypeSearch.done(); NetworkVlanSearch = createSearchBuilder(); - NetworkVlanSearch.and("networkId", NetworkVlanSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkVlanSearch.and("networkId", NetworkVlanSearch.entity().getNetworkId(), SearchCriteria.Op.IN); NetworkVlanSearch.done(); PhysicalNetworkVlanSearch = createSearchBuilder(); @@ -394,10 +396,10 @@ public List listVlansByNetworkId(long networkId) { @Override public List listVlansByNetworkIds(List networkIds) { - SearchBuilder sb = createSearchBuilder(); - sb.and("networkId", sb.entity().getNetworkId(), SearchCriteria.Op.IN); - sb.done(); - SearchCriteria sc = sb.create(); + if (CollectionUtils.isEmpty(networkIds)) { + return Collections.emptyList(); + } + SearchCriteria sc = NetworkVlanSearch.create(); sc.setParameters("networkId", networkIds.toArray()); return listBy(sc); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java index 48c13ff2c5b7..f4f3b0d6ad60 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -18,12 +18,13 @@ import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.Filter; @@ -71,7 +72,7 @@ protected void init() { AllFieldsSearch.and("strategy", AllFieldsSearch.entity().getReservationStrategy(), Op.EQ); AllFieldsSearch.and("strategyNEQ", AllFieldsSearch.entity().getReservationStrategy(), Op.NEQ); AllFieldsSearch.and("reserverName",AllFieldsSearch.entity().getReserver(),Op.EQ); - AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.EQ); + AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.IN); AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ); AllFieldsSearch.and("ipv6Gateway", AllFieldsSearch.entity().getIPv6Gateway(), Op.EQ); AllFieldsSearch.and("ipv6Cidr", AllFieldsSearch.entity().getIPv6Cidr(), Op.EQ); @@ -429,10 +430,10 @@ public NicVO findByMacAddress(String macAddress, long networkId) { @Override public List listByMacAddresses(List macAddresses) { - SearchBuilder sb = createSearchBuilder(); - sb.and("macAddress", sb.entity().getMacAddress(), Op.IN); - sb.done(); - SearchCriteria sc = sb.create(); + if (CollectionUtils.isEmpty(macAddresses)) { + return Collections.emptyList(); + } + SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("macAddress", macAddresses.toArray()); return listBy(sc); } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index c89fec7814dc..5c9d54e19374 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5200,10 +5200,8 @@ public void doInTransactionWithoutResult(TransactionStatus status) { macAddresses.add(entry.getMacAddress()); } Map nicsByMac = new HashMap<>(); - if (!macAddresses.isEmpty()) { - for (NicVO nic : _nicDao.listByMacAddresses(macAddresses)) { - nicsByMac.put(nic.getMacAddress(), nic); - } + for (NicVO nic : _nicDao.listByMacAddresses(macAddresses)) { + nicsByMac.put(nic.getMacAddress(), nic); } Set networkIds = new HashSet<>(); @@ -5211,10 +5209,8 @@ public void doInTransactionWithoutResult(TransactionStatus status) { networkIds.add(nic.getNetworkId()); } Map> vlansByNetwork = new HashMap<>(); - if (!networkIds.isEmpty()) { - for (VlanVO vlan : _vlanDao.listVlansByNetworkIds(new ArrayList<>(networkIds))) { - vlansByNetwork.computeIfAbsent(vlan.getNetworkId(), k -> new ArrayList<>()).add(vlan); - } + for (VlanVO vlan : _vlanDao.listVlansByNetworkIds(new ArrayList<>(networkIds))) { + vlansByNetwork.computeIfAbsent(vlan.getNetworkId(), k -> new ArrayList<>()).add(vlan); } for (VmNetworkStatsEntry vmNetworkStat:vmNetworkStats) {