This is an automated email from the ASF dual-hosted git repository.

nvazquez pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.22 by this push:
     new 99f348e0f94 Batch network lookups per NIC in NetworkOrchestrator to 
eliminate N+1 queries during migration (#13354)
99f348e0f94 is described below

commit 99f348e0f94681a35a03fcb885af61d0c61305f2
Author: Vishesh <[email protected]>
AuthorDate: Mon Oct 19 04:00:47 2026 +0530

    Batch network lookups per NIC in NetworkOrchestrator to eliminate N+1 
queries during migration (#13354)
    
    * Batch network lookups per NIC in NetworkOrchestrator to eliminate N+1 
queries during migration
    
    Three methods called _networksDao.findById(nic.getNetworkId()) per NIC in a 
loop:
    setHypervisorHostname, prepareNicForMigration, prepareAllNicsForMigration.
    Now batch-loads all networks for a VM's NICs in a single WHERE id IN (...) 
query.
    
    - Add NetworkDao.listByIds(List<Long>) for batch ID lookup
    - Add getNetworkMapForNics helper returning Map<Long, NetworkVO>
    - Replace per-NIC findById with map lookup at 3 sites
    - Add null guard for deleted networks (pre-existing NPE risk on all 3 sites)
    - Site 2 (prepare) left unchanged — findById only on error path, not N+1
    
    * Address comments
    
    ---------
    
    Co-authored-by: Aaron Chung <[email protected]>
---
 .../engine/orchestration/NetworkOrchestrator.java  | 29 +++++++++++++++++++---
 .../java/com/cloud/network/dao/NetworkDao.java     |  2 ++
 .../java/com/cloud/network/dao/NetworkDaoImpl.java | 13 ++++++++++
 .../com/cloud/network/dao/NetworkDaoImplTest.java  | 24 ++++++++++++++++++
 .../java/com/cloud/vpc/dao/MockNetworkDaoImpl.java |  5 ++++
 5 files changed, 70 insertions(+), 3 deletions(-)

diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 20b9bf125a4..9b1609c669a 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -2085,13 +2085,26 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
         }
     }
 
+    private Map<Long, NetworkVO> batchLoadNetworksForNics(List<NicVO> nics) {
+        List<Long> networkIds = 
nics.stream().map(NicVO::getNetworkId).distinct().collect(Collectors.toList());
+        Map<Long, NetworkVO> result = new HashMap<>(networkIds.size());
+        for (NetworkVO network : _networksDao.listByIds(networkIds)) {
+            result.put(network.getId(), network);
+        }
+        return result;
+    }
+
     @Override
     public void setHypervisorHostname(VirtualMachineProfile vm, 
DeployDestination dest, boolean migrationSuccessful) throws 
ResourceUnavailableException {
         String hypervisorHostName = 
VirtualMachineManager.getHypervisorHostname(dest.getHost().getName());
         if (StringUtils.isNotEmpty(hypervisorHostName)) {
             final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
+            final Map<Long, NetworkVO> networksById = 
batchLoadNetworksForNics(nics);
             for (final NicVO nic : nics) {
-                final NetworkVO network = 
_networksDao.findById(nic.getNetworkId());
+                final NetworkVO network = networksById.get(nic.getNetworkId());
+                if (network == null) {
+                    continue;
+                }
                 final Integer networkRate = 
_networkModel.getNetworkRate(network.getId(), vm.getId());
                 final NicProfile profile = new NicProfile(nic, network, 
nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, 
_networkModel.isSecurityGroupSupportedInNetwork(network),
                         _networkModel.getNetworkTag(vm.getHypervisorType(), 
network));
@@ -2266,8 +2279,13 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
         }
         final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
         final ReservationContext context = new 
ReservationContextImpl(UUID.randomUUID().toString(), null, null);
+        final Map<Long, NetworkVO> networksById = 
batchLoadNetworksForNics(nics);
         for (final NicVO nic : nics) {
-            final NetworkVO network = 
_networksDao.findById(nic.getNetworkId());
+            final NetworkVO network = networksById.get(nic.getNetworkId());
+            if (network == null) {
+                logger.warn("Network {} not found for nic {} during migration 
prep, skipping", nic.getNetworkId(), nic);
+                continue;
+            }
             final Integer networkRate = 
_networkModel.getNetworkRate(network.getId(), vm.getId());
 
             final NetworkGuru guru = 
AdapterBase.getAdapterByName(networkGurus, network.getGuruName());
@@ -2316,9 +2334,14 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
     public void prepareAllNicsForMigration(final VirtualMachineProfile vm, 
final DeployDestination dest) {
         final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
         final ReservationContext context = new 
ReservationContextImpl(UUID.randomUUID().toString(), null, null);
+        final Map<Long, NetworkVO> networksById = 
batchLoadNetworksForNics(nics);
         Long guestNetworkId = null;
         for (final NicVO nic : nics) {
-            final NetworkVO network = 
_networksDao.findById(nic.getNetworkId());
+            final NetworkVO network = networksById.get(nic.getNetworkId());
+            if (network == null) {
+                logger.warn("Network {} not found for nic {} during migration 
prep, skipping", nic.getNetworkId(), nic);
+                continue;
+            }
             if (network.getTrafficType().equals(TrafficType.Guest) && 
network.getGuestType().equals(GuestType.Isolated)) {
                 guestNetworkId = network.getId();
             }
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java 
b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
index fdca6e43f00..4ef1489a25c 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
@@ -32,6 +32,8 @@ public interface NetworkDao extends GenericDao<NetworkVO, 
Long>, StateDao<State,
 
     List<NetworkVO> listByOwner(long ownerId);
 
+    List<NetworkVO> listByIds(List<Long> ids);
+
     List<NetworkVO> listByGuestType(GuestType type);
 
     List<NetworkVO> listBy(long accountId, long offeringId, long dataCenterId);
diff --git 
a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
index 5887498e094..1999a58fc84 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
@@ -563,6 +563,19 @@ public class NetworkDaoImpl extends 
GenericDaoBase<NetworkVO, Long>implements Ne
         return listBy(sc);
     }
 
+    @Override
+    public List<NetworkVO> listByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return new ArrayList<>();
+        }
+        SearchBuilder<NetworkVO> sb = createSearchBuilder();
+        sb.and("id", sb.entity().getId(), Op.IN);
+        sb.done();
+        SearchCriteria<NetworkVO> sc = sb.create();
+        sc.setParameters("id", ids.toArray());
+        return listBy(sc);
+    }
+
     @Override
     public void addDomainToNetwork(final long networkId, final long domainId, 
final Boolean subdomainAccess) {
         addDomainToNetworknetwork(networkId, domainId, subdomainAccess);
diff --git 
a/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java 
b/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java
index ab5f4352105..b92066dbd94 100644
--- a/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java
+++ b/engine/schema/src/test/java/com/cloud/network/dao/NetworkDaoImplTest.java
@@ -26,11 +26,13 @@ import com.cloud.utils.db.TransactionLegacy;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
 
+import java.util.Collections;
 import java.util.List;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -68,4 +70,26 @@ public class NetworkDaoImplTest {
             txn.close();
         }
     }
+
+    @Test
+    public void listByIdsTestNullOrEmptyIdsReturnsEmptyList() {
+        NetworkDaoImpl networkDaoImplSpy = Mockito.spy(NetworkDaoImpl.class);
+
+        Assert.assertTrue(networkDaoImplSpy.listByIds(null).isEmpty());
+        
Assert.assertTrue(networkDaoImplSpy.listByIds(Collections.emptyList()).isEmpty());
+        Mockito.verify(networkDaoImplSpy, 
Mockito.never()).listBy(Mockito.any(SearchCriteria.class));
+    }
+
+    @Test
+    public void listByIdsTestBuildsInClause() {
+        NetworkDaoImpl networkDaoImplSpy = Mockito.spy(NetworkDaoImpl.class);
+        
Mockito.doReturn(listNetworkVoMock).when(networkDaoImplSpy).listBy(Mockito.any(SearchCriteria.class));
+
+        List<NetworkVO> result = networkDaoImplSpy.listByIds(List.of(1L, 2L, 
3L));
+
+        Assert.assertEquals(listNetworkVoMock, result);
+        ArgumentCaptor<SearchCriteria> scCaptor = 
ArgumentCaptor.forClass(SearchCriteria.class);
+        Mockito.verify(networkDaoImplSpy).listBy(scCaptor.capture());
+        Assert.assertEquals("networks.id IN (?,?,?)", 
scCaptor.getValue().getWhereClause().trim());
+    }
 }
diff --git a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java 
b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java
index 8a0bec56df7..b1d7e82839e 100644
--- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java
+++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java
@@ -280,4 +280,9 @@ public class MockNetworkDaoImpl extends 
GenericDaoBase<NetworkVO, Long> implemen
     public List<NetworkVO> listByNetworkDomainsAndDomainIds(Set<String> 
uniqueNtwkDomains, Set<Long> domainIds) {
         return List.of();
     }
+
+    @Override
+    public List<NetworkVO> listByIds(List<Long> ids) {
+        return List.of();
+    }
 }

Reply via email to