Merge ~bjornt/maas:bug-1873430-2.7 into maas:2.7

Proposed by Björn Tillenius
Status: Merged
Approved by: Björn Tillenius
Approved revision: 7e34ee00ee4facfdd28b344af580e4a3df26fa4f
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~bjornt/maas:bug-1873430-2.7
Merge into: maas:2.7
Diff against target: 126 lines (+46/-11)
4 files modified
src/maasserver/websockets/handlers/fabric.py (+1/-3)
src/maasserver/websockets/handlers/space.py (+12/-8)
src/maasserver/websockets/handlers/tests/test_fabric.py (+18/-0)
src/maasserver/websockets/handlers/tests/test_space.py (+15/-0)
Reviewer Review Type Date Requested Status
Björn Tillenius Approve
Review via email: mp+382678@code.launchpad.net

Commit message

LP #1873430: Subnet pages take a long time to load.

Speed up space.list websocket handler when there are many ip addresses.

The space.list handler prefetched staticipaddress, which make scales
very badly with the number of statipaddress records.

Added tests to ensure that the number of queries is constant and
modified the prefetch and logic to make it pass.

Also remove unneeded prefetch for fabric.list websocket call.

(cherry picked from commit 4cffcfabb2c9e4bfedd8d05c67cdffc2da01ff2a)

To post a comment you must log in.
Revision history for this message
Björn Tillenius (bjornt) wrote :

Self-approve backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/websockets/handlers/fabric.py b/src/maasserver/websockets/handlers/fabric.py
2index 2c70ec3..65d5529 100644
3--- a/src/maasserver/websockets/handlers/fabric.py
4+++ b/src/maasserver/websockets/handlers/fabric.py
5@@ -15,9 +15,7 @@ from maasserver.websockets.handlers.timestampedmodel import (
6
7 class FabricHandler(TimestampedModelHandler):
8 class Meta:
9- queryset = Fabric.objects.all().prefetch_related(
10- "vlan_set__interface_set"
11- )
12+ queryset = Fabric.objects.all().prefetch_related("vlan_set")
13 pk = "id"
14 form = FabricForm
15 form_requires_request = False
16diff --git a/src/maasserver/websockets/handlers/space.py b/src/maasserver/websockets/handlers/space.py
17index ac4c715..f31656c 100644
18--- a/src/maasserver/websockets/handlers/space.py
19+++ b/src/maasserver/websockets/handlers/space.py
20@@ -5,6 +5,8 @@
21
22 __all__ = ["SpaceHandler"]
23
24+import itertools
25+
26 from maasserver.forms.space import SpaceForm
27 from maasserver.models.space import Space
28 from maasserver.permissions import NodePermission
29@@ -15,9 +17,7 @@ from maasserver.websockets.handlers.timestampedmodel import (
30
31 class SpaceHandler(TimestampedModelHandler):
32 class Meta:
33- queryset = Space.objects.all().prefetch_related(
34- "vlan_set__subnet_set__staticipaddress_set__interface_set"
35- )
36+ queryset = Space.objects.all().prefetch_related("vlan_set__subnet_set")
37 pk = "id"
38 form = SpaceForm
39 form_requires_request = False
40@@ -33,11 +33,15 @@ class SpaceHandler(TimestampedModelHandler):
41
42 def dehydrate(self, obj, data, for_list=False):
43 data["name"] = obj.get_name()
44- data["vlan_ids"] = list(
45- obj.vlan_set.order_by("id").values_list("id", flat=True)
46- )
47- data["subnet_ids"] = list(
48- obj.subnet_set.order_by("id").values_list("id", flat=True)
49+ vlans = obj.vlan_set.all()
50+ data["vlan_ids"] = sorted(vlan.id for vlan in vlans)
51+ data["subnet_ids"] = sorted(
52+ itertools.chain(
53+ *[
54+ [subnet.id for subnet in vlan.subnet_set.all()]
55+ for vlan in vlans
56+ ]
57+ )
58 )
59 return data
60
61diff --git a/src/maasserver/websockets/handlers/tests/test_fabric.py b/src/maasserver/websockets/handlers/tests/test_fabric.py
62index b7e35dc..084469b 100644
63--- a/src/maasserver/websockets/handlers/tests/test_fabric.py
64+++ b/src/maasserver/websockets/handlers/tests/test_fabric.py
65@@ -10,6 +10,7 @@ from maasserver.testing.factory import factory
66 from maasserver.testing.testcase import MAASServerTestCase
67 from maasserver.websockets.base import dehydrate_datetime
68 from maasserver.websockets.handlers.fabric import FabricHandler
69+from maastesting.djangotestcase import count_queries
70
71
72 class TestFabricHandler(MAASServerTestCase):
73@@ -61,3 +62,20 @@ class TestFabricHandler(MAASServerTestCase):
74 self.dehydrate_fabric(fabric) for fabric in Fabric.objects.all()
75 ]
76 self.assertItemsEqual(expected_fabrics, handler.list({}))
77+
78+ def test_list_constant_queries(self):
79+ user = factory.make_User()
80+ handler = FabricHandler(user, {}, None)
81+ for _ in range(10):
82+ fabric = factory.make_Fabric()
83+ vlan = fabric.get_default_vlan()
84+ for _ in range(3):
85+ node = factory.make_Node(interface=True)
86+ interface = node.get_boot_interface()
87+ interface.vlan = vlan
88+ interface.save()
89+
90+ queries_one, _ = count_queries(handler.list, {"limit": 1})
91+ queries_multiple, _ = count_queries(handler.list, {})
92+
93+ self.assertEqual(queries_one, queries_multiple)
94diff --git a/src/maasserver/websockets/handlers/tests/test_space.py b/src/maasserver/websockets/handlers/tests/test_space.py
95index e72d7d0..7f0df34 100644
96--- a/src/maasserver/websockets/handlers/tests/test_space.py
97+++ b/src/maasserver/websockets/handlers/tests/test_space.py
98@@ -14,6 +14,7 @@ from maasserver.testing.testcase import MAASServerTestCase
99 from maasserver.utils.orm import reload_object
100 from maasserver.websockets.base import dehydrate_datetime
101 from maasserver.websockets.handlers.space import SpaceHandler
102+from maastesting.djangotestcase import count_queries
103
104
105 class TestSpaceHandler(MAASServerTestCase):
106@@ -53,6 +54,20 @@ class TestSpaceHandler(MAASServerTestCase):
107 ]
108 self.assertItemsEqual(expected_spaces, handler.list({}))
109
110+ def test_list_constant_queries(self):
111+ user = factory.make_User()
112+ handler = SpaceHandler(user, {}, None)
113+ for _ in range(10):
114+ space = factory.make_Space()
115+ node = factory.make_Node(interface=True)
116+ interface = node.get_boot_interface()
117+ subnet = factory.make_Subnet(space=space, vlan=interface.vlan)
118+ factory.make_StaticIPAddress(subnet=subnet, interface=interface)
119+ queries_one, _ = count_queries(handler.list, {"limit": 1})
120+ queries_multiple, _ = count_queries(handler.list, {})
121+
122+ self.assertEqual(queries_one, queries_multiple)
123+
124
125 class TestSpaceHandlerDelete(MAASServerTestCase):
126 def test__delete_as_admin_success(self):

Subscribers

People subscribed via source and target branches