Merge ~bjornt/maas:baseline-test into maas:machine-list-spike

Proposed by Björn Tillenius
Status: Merged
Approved by: Björn Tillenius
Approved revision: 19c56f161b0fc719b031f4980027435529fafec1
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~bjornt/maas:baseline-test
Merge into: maas:machine-list-spike
Diff against target: 199 lines (+165/-0)
5 files modified
setup.cfg (+1/-0)
src/maasperf/tests/maasspike/__init__.py (+0/-0)
src/maasperf/tests/maasspike/test_listing.py (+123/-0)
src/maasspike/__init__.py (+7/-0)
src/maasspike/baseline.py (+34/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Needs Fixing
Alberto Donato (community) Approve
Review via email: mp+433941@code.launchpad.net

Commit message

Add the performance test for the original machine list implementation.

It's meant to be used as a baseline, to which we can compare the other
implementations.

This commit also adds the basic infrastructure for writing the tests.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b baseline-test lp:~bjornt/maas/+git/maas into -b machine-list-spike lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/1536/consoleText
COMMIT: cddede0fb9129ce413bb01a25d2b8a6ee9c33a48

review: Needs Fixing
Revision history for this message
Alberto Donato (ack) wrote :

nice, +1

one suggestion inline

review: Approve
~bjornt/maas:baseline-test updated
19c56f1... by Björn Tillenius

Compare ids instead of the length.

Revision history for this message
Björn Tillenius (bjornt) :
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b baseline-test lp:~bjornt/maas/+git/maas into -b machine-list-spike lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/1543/consoleText
COMMIT: 19c56f161b0fc719b031f4980027435529fafec1

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/setup.cfg b/setup.cfg
index cb3e5db..b61150e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -88,6 +88,7 @@ lint_files =
88 src/maascli88 src/maascli
89 src/maasperf89 src/maasperf
90 src/maasserver90 src/maasserver
91 src/maasspike
91 src/maastesting92 src/maastesting
92 src/metadataserver93 src/metadataserver
93 src/provisioningserver94 src/provisioningserver
diff --git a/src/maasperf/tests/maasspike/__init__.py b/src/maasperf/tests/maasspike/__init__.py
94new file mode 10064495new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/maasperf/tests/maasspike/__init__.py
diff --git a/src/maasperf/tests/maasspike/test_listing.py b/src/maasperf/tests/maasspike/test_listing.py
95new file mode 10064496new file mode 100644
index 0000000..36c9fbd
--- /dev/null
+++ b/src/maasperf/tests/maasspike/test_listing.py
@@ -0,0 +1,123 @@
1"""Performance tests for the machine listing spike.
2
3All tests that test the performance of the different implementations for the
4machine listing spike should go in this file.
5
6For each implemenation, we should have a test that lists all machines, and
7a test that list 50 machines.
8
9Each test should measure how long it takes to produce the list, and then
10assert that the listing is the same as the original websocket handler.
11"""
12
13import pytest
14
15from maasserver.models import Machine
16from maasserver.websockets.handlers.machine import MachineHandler
17from maasspike import baseline
18
19
20class ExpectedMachines:
21 def __init__(self, expected_list):
22 self._expected_list = expected_list
23
24 def assert_list(self, machine_list, limit=None):
25 """Assert that the passed in machine list is correct.
26
27 Compare the number of machines in each list, as well as making
28 sure that the first and last machine of each list are equal.
29
30 Comparing the full list takes too long if you have a list of
31 1000 machines.
32 """
33 assert isinstance(machine_list, list)
34 expected_list = (
35 self._expected_list[:limit] if limit else self._expected_list
36 )
37 assert [machine["id"] for machine in machine_list] == [
38 machine["id"] for machine in expected_list
39 ]
40 assert machine_list[0] == expected_list[0]
41 assert machine_list[-1] == expected_list[-1]
42
43
44def get_expected_machines(admin):
45 """Get the list of machines that the normal websocket handler returns."""
46 machine_count = Machine.objects.all().count()
47 ws_handler = MachineHandler(admin, {}, None)
48 params = {
49 "filter": {},
50 "page_number": 1,
51 "page_size": machine_count + 1,
52 "sort_direction": "ascending",
53 "sort_key": "hostname",
54 }
55 result = ws_handler.list(params)
56 return ExpectedMachines(result["groups"][0]["items"])
57
58
59@pytest.fixture(scope="session")
60def _expected():
61 """Helper fixture to store the expected machine list for the session.
62
63 A session fixture doesn't have access to the DB, so we make use of a
64 function fixture, expected_machines, to get the machine listing
65 for the first test and store it here.
66
67 The fixture shouldn't be used by any test.
68 """
69 return {}
70
71
72@pytest.fixture
73def expected_machines(admin, _expected):
74 if "machines" not in _expected:
75 _expected["machines"] = get_expected_machines(admin)
76 return _expected["machines"]
77
78
79def test_populate_expected_machines(expected_machines):
80 """A blank test to populate the expected_machines fixture.
81
82 This should be the first test to be run, so that other test
83 don't get any advantage by having the query to the machine
84 listing cached.
85 """
86
87
88@pytest.mark.parametrize("limit", [None, 50])
89class TestListing:
90 """Collection of tests for spike machine listing implementations.
91
92 A class is used to group the tests together to ensure they measure
93 and assert the same thing.
94
95 Each implementation should have a test that does the required setup
96 and then call self.run_listing_test.
97
98 Each test is run once using the full listing, and once using the first
99 50 machines.
100 """
101
102 @pytest.fixture(autouse=True)
103 def set_up(self, perf, admin, expected_machines):
104 self._expected_machines = expected_machines
105 self._admin = admin
106 self._perf = perf
107 yield
108 self._expected_machines = None
109 self._admin = None
110 self._perf = None
111
112 def run_listing_test(self, name, func, limit):
113 record_name = name
114 if limit:
115 record_name += f"_{limit}"
116 else:
117 record_name += "_all"
118 with self._perf.record(record_name):
119 machines = func(self._admin, limit)
120 self._expected_machines.assert_list(machines, limit)
121
122 def test_baseline(self, limit):
123 self.run_listing_test("baseline", baseline.list_machines, limit)
diff --git a/src/maasspike/__init__.py b/src/maasspike/__init__.py
0new file mode 100644124new file mode 100644
index 0000000..5476dfd
--- /dev/null
+++ b/src/maasspike/__init__.py
@@ -0,0 +1,7 @@
1"""This package contains experimental code for the websocket machine listing.
2
3We're doing a spike on how to improve the rendering time of the machine
4listing in the UI. We keep the code in a separate package to make it
5clear what's part of the spike, and any improvements we make that
6can be merged back to master.
7"""
diff --git a/src/maasspike/baseline.py b/src/maasspike/baseline.py
0new file mode 1006448new file mode 100644
index 0000000..f3a6d84
--- /dev/null
+++ b/src/maasspike/baseline.py
@@ -0,0 +1,34 @@
1from maasserver.websockets.handlers.machine import MachineHandler
2
3
4class NoPaginationMachineHandler(MachineHandler):
5 """A machine handler that returns only the machines.
6
7 It doesn't do any grouping, searching, or other things
8 (like caching the ids) which might slow it down.
9
10 The purpose of this is to use our current implementation as a baseline
11 for other implementations to beat. We reuse the current websocket
12 handler as much as possible, so that any improvements to it will
13 be shown here as well.
14 """
15
16 def list(self, params):
17 qs = self.get_queryset(for_list=True)
18 qs = self._sort(qs, "list", params)
19 limit = params.get("limit")
20 if limit:
21 qs = qs[:limit]
22 objs = list(qs)
23 # This is needed to calculate the script result summaries.
24 # It's quite expensive.
25 self._cache_script_results(objs)
26 return [self.full_dehydrate(obj, for_list=True) for obj in objs]
27
28
29def list_machines(admin, limit=None):
30 ws_handler = NoPaginationMachineHandler(admin, {}, None)
31 params = {}
32 if limit:
33 params["limit"] = limit
34 return ws_handler.list(params)

Subscribers

People subscribed via source and target branches