Merge lp:~allenap/maas/fewer-power-params-per-batch into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Andres Rodriguez
Approved revision: no longer in the source branch.
Merged at revision: 5948
Proposed branch: lp:~allenap/maas/fewer-power-params-per-batch
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 148 lines (+51/-5)
4 files modified
src/maasserver/rpc/nodes.py (+10/-2)
src/maasserver/rpc/tests/test_nodes.py (+24/-3)
src/maasserver/testing/factory.py (+1/-0)
src/maasserver/testing/tests/test_factory.py (+16/-0)
To merge this branch: bzr merge lp:~allenap/maas/fewer-power-params-per-batch
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+322089@code.launchpad.net

Commit message

Limit batches of node power parameters to 10 for the periodic power checking service.

This is intended to reduce the likelihood of transactional conflicts.

Description of the change

When each batch of node power parameters is fetched from the region by a rack's power monitor service there is a sting in the tail: every row in maasserver_node that corresponds to those parameters is updated. This appears to be increasing transactional conflicts in the database to the point where it exceeds 10 retries and the transaction is fully rejected.

A better way to fix this would be to not update those rows, or to have a separate table to record last-queried times, but that's a lot more work and there isn't time right now, so instead we limit the batch size to 10.

This also fixes a long-standing bug in Factory.make_Node where the given power parameters were being ignored when also using bmc_connected_to. This fix improves the speed of one particular test, taking it from ~15 seconds to <1 second.

To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm! Should this be attached to the bug report ?

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/rpc/nodes.py'
--- src/maasserver/rpc/nodes.py 2017-01-28 00:51:47 +0000
+++ src/maasserver/rpc/nodes.py 2017-04-06 08:23:19 +0000
@@ -11,7 +11,10 @@
11]11]
1212
13from datetime import timedelta13from datetime import timedelta
14from itertools import chain14from itertools import (
15 chain,
16 islice,
17)
15import json18import json
1619
17from django.contrib.auth.models import User20from django.contrib.auth.models import User
@@ -135,11 +138,15 @@
135138
136@synchronous139@synchronous
137@transactional140@transactional
138def list_cluster_nodes_power_parameters(system_id):141def list_cluster_nodes_power_parameters(system_id, limit=10):
139 """Return power parameters that a rack controller should power check,142 """Return power parameters that a rack controller should power check,
140 in priority order.143 in priority order.
141144
142 For :py:class:`~provisioningserver.rpc.region.ListNodePowerParameters`.145 For :py:class:`~provisioningserver.rpc.region.ListNodePowerParameters`.
146
147 :param limit: Limit the number of nodes for which to return power
148 parameters. Pass `None` to remove this numerical limit; there is still
149 a limit on the quantity of power information that will be returned.
143 """150 """
144 try:151 try:
145 rack = RackController.objects.get(system_id=system_id)152 rack = RackController.objects.get(system_id=system_id)
@@ -149,6 +156,7 @@
149 # Generate all the the power queries that will fit into the response.156 # Generate all the the power queries that will fit into the response.
150 nodes = rack.get_bmc_accessible_nodes()157 nodes = rack.get_bmc_accessible_nodes()
151 details = _gen_cluster_nodes_power_parameters(nodes)158 details = _gen_cluster_nodes_power_parameters(nodes)
159 details = islice(details, limit) # ... but never more than `limit`.
152 details = _gen_up_to_json_limit(details, 60 * (2 ** 10)) # 60kiB160 details = _gen_up_to_json_limit(details, 60 * (2 ** 10)) # 60kiB
153 details = list(details)161 details = list(details)
154162
155163
=== modified file 'src/maasserver/rpc/tests/test_nodes.py'
--- src/maasserver/rpc/tests/test_nodes.py 2017-03-31 22:57:48 +0000
+++ src/maasserver/rpc/tests/test_nodes.py 2017-04-06 08:23:19 +0000
@@ -61,6 +61,7 @@
61from testtools.matchers import (61from testtools.matchers import (
62 Equals,62 Equals,
63 GreaterThan,63 GreaterThan,
64 HasLength,
64 Is,65 Is,
65 LessThan,66 LessThan,
66 Not,67 Not,
@@ -469,7 +470,7 @@
469 system_ids)470 system_ids)
470471
471 def test__returns_at_most_60kiB_of_JSON(self):472 def test__returns_at_most_60kiB_of_JSON(self):
472 # Configure the rack controller subnt to be very large so it473 # Configure the rack controller subnet to be very large so it
473 # can hold that many BMC connected to the interface for the rack474 # can hold that many BMC connected to the interface for the rack
474 # controller.475 # controller.
475 rack = factory.make_RackController(power_type='')476 rack = factory.make_RackController(power_type='')
@@ -482,14 +483,15 @@
482483
483 # Ensure that there are at least 64kiB of power parameters (when484 # Ensure that there are at least 64kiB of power parameters (when
484 # converted to JSON) in the database.485 # converted to JSON) in the database.
485 example_parameters = {"key%d" % i: "value%d" % i for i in range(100)}486 example_parameters = {"key%d" % i: "value%d" % i for i in range(250)}
486 remaining = 2 ** 16487 remaining = 2 ** 16
487 while remaining > 0:488 while remaining > 0:
488 node = self.make_Node(489 node = self.make_Node(
489 bmc_connected_to=rack, power_parameters=example_parameters)490 bmc_connected_to=rack, power_parameters=example_parameters)
490 remaining -= len(json.dumps(node.get_effective_power_parameters()))491 remaining -= len(json.dumps(node.get_effective_power_parameters()))
491492
492 nodes = list_cluster_nodes_power_parameters(rack.system_id)493 nodes = list_cluster_nodes_power_parameters(
494 rack.system_id, limit=None) # Remove numeric limit.
493495
494 # The total size of the JSON is less than 60kiB, but only a bit.496 # The total size of the JSON is less than 60kiB, but only a bit.
495 nodes_json = map(json.dumps, nodes)497 nodes_json = map(json.dumps, nodes)
@@ -500,6 +502,25 @@
500 expected_minimum = 50 * (2 ** 10) # 50kiB502 expected_minimum = 50 * (2 ** 10) # 50kiB
501 self.expectThat(nodes_json_length, GreaterThan(expected_minimum - 1))503 self.expectThat(nodes_json_length, GreaterThan(expected_minimum - 1))
502504
505 def test__limited_to_10_nodes_at_a_time_by_default(self):
506 # Configure the rack controller subnet to be large enough.
507 rack = factory.make_RackController(power_type='')
508 rack_interface = rack.get_boot_interface()
509 subnet = factory.make_Subnet(
510 cidr=str(factory.make_ipv6_network(slash=8)))
511 factory.make_StaticIPAddress(
512 ip=factory.pick_ip_in_Subnet(subnet), subnet=subnet,
513 interface=rack_interface)
514
515 # Create at least 11 nodes connected to the rack.
516 for _ in range(11):
517 self.make_Node(bmc_connected_to=rack)
518
519 # Only 10 nodes' power parameters are returned.
520 self.assertThat(
521 list_cluster_nodes_power_parameters(rack.system_id),
522 HasLength(10))
523
503524
504class TestUpdateNodePowerState(MAASServerTestCase):525class TestUpdateNodePowerState(MAASServerTestCase):
505526
506527
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2017-04-03 19:46:59 +0000
+++ src/maasserver/testing/factory.py 2017-04-06 08:23:19 +0000
@@ -456,6 +456,7 @@
456 ip_address = existing_static_ips[0]456 ip_address = existing_static_ips[0]
457 bmc_ip_address = self.pick_ip_in_Subnet(ip_address.subnet)457 bmc_ip_address = self.pick_ip_in_Subnet(ip_address.subnet)
458 node.power_parameters = {458 node.power_parameters = {
459 **node.power_parameters,
459 "power_address": "qemu+ssh://user@%s/system" % (460 "power_address": "qemu+ssh://user@%s/system" % (
460 factory.ip_to_url_format(bmc_ip_address)),461 factory.ip_to_url_format(bmc_ip_address)),
461 "power_id": factory.make_name("power_id"),462 "power_id": factory.make_name("power_id"),
462463
=== modified file 'src/maasserver/testing/tests/test_factory.py'
--- src/maasserver/testing/tests/test_factory.py 2016-09-22 02:53:33 +0000
+++ src/maasserver/testing/tests/test_factory.py 2017-04-06 08:23:19 +0000
@@ -12,6 +12,7 @@
12from maasserver.utils.orm import reload_object12from maasserver.utils.orm import reload_object
13from testtools.matchers import (13from testtools.matchers import (
14 Contains,14 Contains,
15 ContainsDict,
15 Equals,16 Equals,
16)17)
1718
@@ -99,3 +100,18 @@
99 self.assertThat(100 self.assertThat(
100 {iface.vlan for iface in sip.interface_set.all()},101 {iface.vlan for iface in sip.interface_set.all()},
101 Contains(iface.vlan))102 Contains(iface.vlan))
103
104
105class TestFactoryForNodes(MAASServerTestCase):
106
107 def test_make_Node_uses_power_parameters_when_connecting_to_BMC(self):
108 p_key = factory.make_name("key")
109 p_value = factory.make_name("value")
110 node = factory.make_Node(
111 bmc_connected_to=factory.make_RackController(),
112 power_parameters={p_key: p_value},
113 )
114 self.assertThat(
115 node.get_effective_power_parameters(),
116 ContainsDict({p_key: Equals(p_value)}),
117 )