Allow switchover only to sync nodes when synchronous replication is on (#2076)

Close https://github.com/zalando/patroni/issues/2074
This commit is contained in:
Alexander Kukushkin
2021-10-04 16:23:45 +02:00
committed by GitHub
parent a431f50378
commit 1c2bf258d6
2 changed files with 13 additions and 4 deletions
+8 -4
View File
@@ -317,7 +317,7 @@ class Ha(object):
if timeout == 0:
# We are requested to prefer failing over to restarting master. But see first if there
# is anyone to fail over to.
if self.is_failover_possible(self.cluster.members, True):
if self.is_failover_possible(self.cluster.members):
logger.info("Master crashed. Failing over.")
self.demote('immediate')
return 'stopped PostgreSQL to fail over after a crash'
@@ -782,6 +782,10 @@ class Ha(object):
return False
if self.cluster.failover:
# When doing a switchover in synchronous mode only synchronous nodes and former leader are allowed to race
if self.is_synchronous_mode() and self.cluster.failover.leader and \
self.cluster.failover.candidate and not self.cluster.sync.matches(self.state_handler.name):
return False
return self.manual_failover_process_no_leader()
if not self.watchdog.is_healthy:
@@ -925,7 +929,7 @@ class Ha(object):
else:
members = [m for m in self.cluster.members
if not failover.candidate or m.name == failover.candidate]
if self.is_failover_possible(members): # check that there are healthy members
if self.is_failover_possible(members, False): # check that there are healthy members
ret = self._async_executor.try_run_async('manual failover: demote', self.demote, ('graceful',))
return ret or 'manual failover: demoting myself'
else:
@@ -1179,7 +1183,7 @@ class Ha(object):
if self.has_lock() and self.update_lock():
if self._async_executor.scheduled_action == 'doing crash recovery in a single user mode':
time_left = self.patroni.config['master_start_timeout'] - (time.time() - self._crash_recovery_started)
if time_left <= 0 and self.is_failover_possible(self.cluster.members, True):
if time_left <= 0 and self.is_failover_possible(self.cluster.members):
logger.info("Demoting self because crash recovery is taking too long")
self.state_handler.cancellable.cancel(True)
self.demote('immediate')
@@ -1286,7 +1290,7 @@ class Ha(object):
time_left = timeout - self.state_handler.time_in_state()
if time_left <= 0:
if self.is_failover_possible(self.cluster.members, True):
if self.is_failover_possible(self.cluster.members):
logger.info("Demoting self because master startup is taking too long")
self.demote('immediate')
return 'stopped PostgreSQL because of startup timeout'
+5
View File
@@ -624,6 +624,11 @@ class TestHa(PostgresInit):
# same as previous, but set the current member to nofailover. In no case it should be elected as a leader
self.ha.patroni.nofailover = True
self.assertEqual(self.ha.run_cycle(), 'following a different leader because I am not allowed to promote')
# in sync mode only the sync node is allowed to take over
self.ha.cluster = get_cluster_initialized_without_leader(failover=Failover(0, 'leader', 'other', None))
self.ha.patroni.nofailover = False
self.ha.is_synchronous_mode = true
self.assertEqual(self.ha.run_cycle(), 'following a different leader because i am not the healthiest node')
def test_manual_failover_process_no_leader_in_pause(self):
self.ha.is_paused = true