From ed7bfa11ded509466a8d07ebe952c21621a22c2d Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Mon, 29 Nov 2021 09:40:42 +0100 Subject: [PATCH] Avoid writing segments to the DB repeatedly When: * the segments service plugin is enabled and * we have multiple rpc worker processes (as in the sum of rpc_workers and rpc_state_report_workers, since both kind processes agent state_reports) and * many ovs-agents report physnets, then rabbitmq dispatches the state_report messages between the workers in a round robin fashion, therefore eventually the state_reports of the same agent will hit all rpc workers. Unfortunately all worker processes have a 'reported_hosts' set to remember from which host it has seen agent reports already. But right after a server start when that set is still empty, each worker will unconditionally write the received physnet-segment information into the db. This means we multiply the load on the db and rpc workers by a factor of the rpc worker count. This patch tries to reduce the load on the db by adding another early return before the unconditional db write. Change-Id: I935186b6ee95f0cae8dc05869d9742c8fb3353c3 Closes-Bug: #1952730 (cherry picked from commit 176503e610aee16cb5799a77466579bc55129450) (cherry picked from commit dcb372b041a97121027706ca18c616adfc07d243) (cherry picked from commit 0c909e3b55c0f4d38647fa54882f8cbfd85f662a) --- neutron/db/agents_db.py | 4 +++- neutron/services/segments/db.py | 5 +++++ neutron/tests/unit/extensions/test_segment.py | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/neutron/db/agents_db.py b/neutron/db/agents_db.py index b3cbff5621c..36d8fd1c66d 100644 --- a/neutron/db/agents_db.py +++ b/neutron/db/agents_db.py @@ -397,6 +397,7 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin): agent = self._get_agent_by_type_and_host( context, agent_state['agent_type'], agent_state['host']) agent_state_orig = copy.deepcopy(agent_state) + agent_state_previous = copy.deepcopy(agent) if not agent.is_active: status = agent_consts.AGENT_REVIVED if 'resource_versions' not in agent_state: @@ -417,6 +418,7 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin): event_type = events.AFTER_UPDATE except agent_exc.AgentNotFoundByTypeHost: agent_state_orig = None + agent_state_previous = None greenthread.sleep(0) res['created_at'] = current_time res['started_at'] = current_time @@ -441,7 +443,7 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin): 'plugin': self, 'status': status }, - states=(agent_state_orig, ), + states=(agent_state_orig, agent_state_previous), desired_state=agent_state, resource_id=agent.id )) diff --git a/neutron/services/segments/db.py b/neutron/services/segments/db.py index e082b2adece..66113677a90 100644 --- a/neutron/services/segments/db.py +++ b/neutron/services/segments/db.py @@ -298,6 +298,11 @@ def _update_segment_host_mapping_for_agent(resource, event, trigger, if host in reported_hosts and not start_flag: return reported_hosts.add(host) + if (len(payload.states) > 1 and + payload.states[1] is not None and + agent.get('configurations') == payload.states[1].get( + 'configurations')): + return segments = get_segments_with_phys_nets(context, phys_nets) current_segment_ids = { segment['id'] for segment in segments diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index ecd21e8a85d..84bdee5ce28 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -947,9 +947,9 @@ class TestMl2HostSegmentMappingAgentServerSynch(HostSegmentMappingTestCase): self._register_agent(host, mappings={physical_network: 'br-eth-1'}, plugin=self.plugin, start_flag=True) self.assertIn(host, db.reported_hosts) - self.assertEqual(2, mock_function.call_count) + self.assertEqual(1, mock_function.call_count) expected_call = mock.call(mock.ANY, host, set()) - mock_function.assert_has_calls([expected_call, expected_call]) + mock_function.assert_has_calls([expected_call]) @mock.patch(mock_path) def test_no_starting_agent_is_not_processed(self, mock_function):