Ver código fonte

perf(virtualization): Remove unnecessary prefetch from Cluster queryset (#23303)

Removes redundant prefetch_related('virtual_machines') since VM totals
come from annotations without loading related VMs. Reduces query count
from 15 to 14 and includes regression test verifying no VMs are loaded.

Fixes #23291
Martin Hauser 21 horas atrás
pai
commit
3d8dbeaec7

+ 1 - 1
netbox/virtualization/api/views.py

@@ -37,7 +37,7 @@ class ClusterGroupViewSet(NetBoxModelViewSet):
 
 
 class ClusterViewSet(NetBoxModelViewSet):
-    queryset = Cluster.objects.prefetch_related('virtual_machines').annotate(
+    queryset = Cluster.objects.annotate(
         allocated_vcpus=Sum('virtual_machines__vcpus'),
         allocated_memory=Sum('virtual_machines__memory'),
         allocated_disk=Sum('virtual_machines__disk'),

+ 1 - 1
netbox/virtualization/tests/query_counts.json

@@ -1,5 +1,5 @@
 {
-  "cluster:api_list_objects": 15,
+  "cluster:api_list_objects": 14,
   "cluster:list_objects_with_permission": 19,
   "clustergroup:api_list_objects": 12,
   "clustergroup:list_objects_with_permission": 17,

+ 35 - 0
netbox/virtualization/tests/test_api.py

@@ -1,5 +1,7 @@
 import logging
+from decimal import Decimal
 
+from django.db.models.signals import post_init
 from django.test import override_settings, tag
 from django.urls import reverse
 from netaddr import IPNetwork
@@ -176,6 +178,39 @@ class ClusterTestCase(APIViewTestCases.APIViewTestCase):
             },
         ]
 
+    def test_list_vm_totals_without_loading_virtual_machines(self):
+        """
+        VM counts and resource totals come from annotations without loading any virtual machine.
+        """
+        cluster1 = Cluster.objects.get(name='Cluster 1')
+        cluster2 = Cluster.objects.get(name='Cluster 2')
+        VirtualMachine.objects.bulk_create((
+            VirtualMachine(name='Virtual Machine 1', cluster=cluster1, vcpus=Decimal('1.5'), memory=1024, disk=10000),
+            VirtualMachine(name='Virtual Machine 2', cluster=cluster1, vcpus=Decimal('2.25'), memory=2048, disk=20000),
+            VirtualMachine(name='Virtual Machine 3', cluster=cluster1),
+            VirtualMachine(name='Virtual Machine 4', cluster=cluster2),
+        ))
+        self.add_permissions('virtualization.view_cluster')
+        loaded = []
+
+        def record(sender, instance, **kwargs):
+            loaded.append(instance)
+
+        post_init.connect(record, sender=VirtualMachine)
+        try:
+            response = self.client.get(self._get_list_url(), **self.header)
+        finally:
+            post_init.disconnect(record, sender=VirtualMachine)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        fields = ('virtualmachine_count', 'allocated_vcpus', 'allocated_memory', 'allocated_disk')
+        totals = {row['name']: tuple(row[field] for field in fields) for row in response.data['results']}
+        self.assertEqual(totals, {
+            'Cluster 1': (3, Decimal('3.75'), 3072, 30000),
+            'Cluster 2': (1, None, None, None),
+            'Cluster 3': (0, None, None, None),
+        })
+        self.assertEqual(loaded, [])
+
 
 class VirtualMachineTypeTestCase(APIViewTestCases.APIViewTestCase):
     model = VirtualMachineType