diff --git a/nova/conf/compute.py b/nova/conf/compute.py index ac7bd4c9b25d..9a404a6f21be 100644 --- a/nova/conf/compute.py +++ b/nova/conf/compute.py @@ -650,7 +650,8 @@ Related options: """), cfg.IntOpt('resource_provider_association_refresh', default=300, - min=1, + min=0, + mutable=True, help=""" Interval for updating nova-compute-side cache of the compute node resource provider's aggregates and traits info. @@ -659,9 +660,11 @@ This option specifies the number of seconds between attempts to update a provider's aggregates and traits information in the local cache of the compute node. +A value of zero disables cache refresh completely. + Possible values: -* Any positive integer in seconds. +* Any positive integer in seconds, or zero to disable refresh. """), cfg.StrOpt('cpu_shared_set', help=""" diff --git a/nova/scheduler/client/report.py b/nova/scheduler/client/report.py index 278535de90a9..5e825de4ded3 100644 --- a/nova/scheduler/client/report.py +++ b/nova/scheduler/client/report.py @@ -847,10 +847,20 @@ class SchedulerReportClient(object): Associations are stale if association_refresh_time for this uuid is not set or is more than CONF.compute.resource_provider_association_refresh seconds ago. + + Always False if CONF.compute.resource_provider_association_refresh is + zero. """ + rpar = CONF.compute.resource_provider_association_refresh refresh_time = self._association_refresh_time.get(uuid, 0) - return ((time.time() - refresh_time) > - CONF.compute.resource_provider_association_refresh) + # If refresh is disabled, associations are "never" stale. (But still + # load them if we haven't yet done so.) + if rpar == 0 and refresh_time != 0: + # TODO(efried): If refresh is disabled, we could avoid touching the + # _association_refresh_time dict anywhere, but that would take some + # nontrivial refactoring. + return False + return (time.time() - refresh_time) > rpar def _update_inventory_attempt(self, context, rp_uuid, inv_data): """Update the inventory for this resource provider if needed. diff --git a/nova/tests/unit/scheduler/client/test_report.py b/nova/tests/unit/scheduler/client/test_report.py index d08150a69547..adee516985ec 100644 --- a/nova/tests/unit/scheduler/client/test_report.py +++ b/nova/tests/unit/scheduler/client/test_report.py @@ -2934,6 +2934,62 @@ class TestAssociations(SchedulerReportClientTestCase): mock_trait_get.assert_called_once_with(self.context, uuid) mock_shr_get.assert_called_once_with(self.context, set()) + @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' + '_get_provider_aggregates') + @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' + '_get_provider_traits') + @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' + '_get_sharing_providers') + def test_refresh_associations_disabled(self, mock_shr_get, mock_trait_get, + mock_agg_get): + """Test that refresh associations can be disabled.""" + self.flags(resource_provider_association_refresh=0, group='compute') + uuid = uuids.compute_node + # Seed the provider tree so _refresh_associations finds the provider + self.client._provider_tree.new_root('compute', uuid, generation=1) + mock_agg_get.return_value = report.AggInfo(aggregates=set([]), + generation=42) + mock_trait_get.return_value = report.TraitInfo(traits=set([]), + generation=43) + mock_shr_get.return_value = [] + + # Called a first time because association_refresh_time is empty. + now = time.time() + self.client._refresh_associations(self.context, uuid) + mock_agg_get.assert_called_once_with(self.context, uuid) + mock_trait_get.assert_called_once_with(self.context, uuid) + mock_shr_get.assert_called_once_with(self.context, set()) + self.assertIn(uuid, self.client._association_refresh_time) + + # Clear call count. + mock_agg_get.reset_mock() + mock_trait_get.reset_mock() + mock_shr_get.reset_mock() + + with mock.patch('time.time') as mock_future: + # A lot of time passes + mock_future.return_value = now + 10000000000000 + self.client._refresh_associations(self.context, uuid) + mock_agg_get.assert_not_called() + mock_trait_get.assert_not_called() + mock_shr_get.assert_not_called() + + # Forever passes + mock_future.return_value = float('inf') + self.client._refresh_associations(self.context, uuid) + mock_agg_get.assert_not_called() + mock_trait_get.assert_not_called() + mock_shr_get.assert_not_called() + + # Even if no time passes, clearing the counter triggers refresh + mock_future.return_value = now + del self.client._association_refresh_time[uuid] + self.client._refresh_associations(self.context, uuid) + mock_agg_get.assert_called_once_with(self.context, uuid) + mock_trait_get.assert_called_once_with(self.context, uuid) + mock_shr_get.assert_called_once_with(self.context, set()) + self.assertIn(uuid, self.client._association_refresh_time) + class TestComputeNodeToInventoryDict(test.NoDBTestCase): def test_compute_node_inventory(self): diff --git a/releasenotes/notes/disable-rt-cache-refresh-9f6633e585516760.yaml b/releasenotes/notes/disable-rt-cache-refresh-9f6633e585516760.yaml new file mode 100644 index 000000000000..98e1f69e23a2 --- /dev/null +++ b/releasenotes/notes/disable-rt-cache-refresh-9f6633e585516760.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The configuration option + ``[compute]resource_provider_association_refresh`` can now + be set to zero to disable refresh entirely. This follows on from `bug + 1767309`_ allowing more aggressive reduction in the amount of traffic to + the placement service. + + .. _`bug 1767309`: https://bugs.launchpad.net/nova/+bug/1767309