Run only one query per HA loop (#2516)

If the cluster is stable (no nodes are joining/leaving/lagging) we want to run at most one monitor query per every HA loop. So far it worker perfectly except when synchronous_mode is enabled, where we run two additional queries:
1. SHOW synchronous_mode
2. SELECT ... FROM pg_stat_replication

In order to solve it, we will include these "queries" to the common monitoring query is synchronous_mode is enabled.

In addition to that make sure that `synchronous_standby_names` is reset on replicas that used to be a primary and avoid using replicas which are not in the 'running' state.

P.S.: in the monitoring query we also extract the current value of synchronous_standby_names, because it will be useful for the quorum commit feature.

Close https://github.com/zalando/patroni/issues/2469
This commit is contained in:
Alexander Kukushkin
2023-01-10 10:44:17 +01:00
committed by GitHub
parent baaf187c81
commit c12fe4146d
6 changed files with 88 additions and 52 deletions
+22 -22
View File
@@ -638,44 +638,44 @@ class TestPostgresql(BaseTestPostgresql):
self.p._state = 'starting'
self.assertIsNone(self.p.wait_for_startup())
@patch.object(Postgresql, 'last_operation', Mock(return_value=2))
def test_pick_sync_standby(self):
cluster = Cluster(True, None, self.leader, 0, [self.me, self.other, self.leadermem], None,
SyncState(0, self.me.name, self.leadermem.name), None, None, None)
mock_cursor = Mock()
mock_cursor.fetchone.return_value = ('remote_apply',)
with patch.object(Postgresql, "query", side_effect=[
mock_cursor,
[(self.leadermem.name, 'sync', 1),
(self.me.name, 'async', 2),
(self.other.name, 'async', 2)]
with patch.object(Postgresql, "_cluster_info_state_get", side_effect=[
'on',
[{'application_name': self.leadermem.name, 'sync_state': 'sync', 'flush_lsn': 1},
{'application_name': self.me.name, 'sync_state': 'async', 'flush_lsn': 2},
{'application_name': self.other.name, 'sync_state': 'async', 'flush_lsn': 2}]
]):
self.assertEqual(self.p.pick_synchronous_standby(cluster), ([self.leadermem.name], [self.leadermem.name]))
with patch.object(Postgresql, "query", side_effect=[
mock_cursor,
[(self.leadermem.name, 'potential', 1),
(self.me.name, 'async', 2),
(self.other.name, 'async', 2)]
with patch.object(Postgresql, "_cluster_info_state_get", side_effect=[
'remote_write',
[{'application_name': self.leadermem.name, 'sync_state': 'potential', 'write_lsn': 1},
{'application_name': self.me.name, 'sync_state': 'async', 'write_lsn': 2},
{'application_name': self.other.name, 'sync_state': 'async', 'write_lsn': 2}]
]):
self.assertEqual(self.p.pick_synchronous_standby(cluster), ([self.leadermem.name], []))
with patch.object(Postgresql, "query", side_effect=[
mock_cursor,
[(self.me.name, 'async', 1),
(self.other.name, 'async', 2)]
with patch.object(Postgresql, "_cluster_info_state_get", side_effect=[
'remote_apply',
[{'application_name': self.me.name.upper(), 'sync_state': 'async', 'replay_lsn': 2},
{'application_name': self.other.name, 'sync_state': 'async', 'replay_lsn': 1}]
]):
self.assertEqual(self.p.pick_synchronous_standby(cluster), ([self.me.name], []))
with patch.object(Postgresql, "query", side_effect=[
mock_cursor,
[('missing', 'sync', 1),
(self.me.name, 'async', 2),
(self.other.name, 'async', 3)]
with patch.object(Postgresql, "_cluster_info_state_get", side_effect=[
'remote_apply',
[{'application_name': 'missing', 'sync_state': 'sync', 'replay_lsn': 3},
{'application_name': self.me.name, 'sync_state': 'async', 'replay_lsn': 2},
{'application_name': self.other.name, 'sync_state': 'async', 'replay_lsn': 1}]
]):
self.assertEqual(self.p.pick_synchronous_standby(cluster), ([self.me.name], []))
with patch.object(Postgresql, "query", side_effect=[mock_cursor, []]):
with patch.object(Postgresql, "_cluster_info_state_get", side_effect=['remote_apply', []]):
self.p._major_version = 90400
self.assertEqual(self.p.pick_synchronous_standby(cluster), ([], []))