From 03d9226633759aadbaa79ca09aaa7754ffac7338 Mon Sep 17 00:00:00 2001 From: Polina Bungina Date: Tue, 12 Sep 2023 14:02:17 +0200 Subject: [PATCH] Implement Failover.is_failover/switchover properties --- patroni/dcs/__init__.py | 9 ++++++++- patroni/ha.py | 8 ++++---- patroni/utils.py | 5 +++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/patroni/dcs/__init__.py b/patroni/dcs/__init__.py index 4cef65ea..a3d4dbdd 100644 --- a/patroni/dcs/__init__.py +++ b/patroni/dcs/__init__.py @@ -451,7 +451,7 @@ class Leader(NamedTuple): class Failover(NamedTuple): - """Immutable object (namedtuple) representing configuration information required for failover/switchover capability. + """Immutable object (namedtuple) which represents failover key. :ivar version: version of the object. :ivar leader: name of the leader. If value isn't empty we treat it as a switchover from the specified node. @@ -547,6 +547,13 @@ class Failover(NamedTuple): """ return int(bool(self.leader)) + int(bool(self.candidate)) + @property + def is_switchover(self) -> bool: + return bool(self.leader) + + @property + def is_failover(self) -> bool: + return not self.is_switchover class ClusterConfig(NamedTuple): """Immutable object (namedtuple) which represents cluster configuration. diff --git a/patroni/ha.py b/patroni/ha.py index a3b94db9..387c2230 100644 --- a/patroni/ha.py +++ b/patroni/ha.py @@ -234,7 +234,7 @@ class Ha(object): """ if not self.cluster.failover: return 'failover' - return 'switchover' if self.cluster.failover.leader else 'manual failover' + return 'switchover' if self.cluster.failover.is_switchover else 'manual failover' def load_cluster_from_dcs(self) -> None: cluster = self.dcs.get_cluster() @@ -1044,7 +1044,7 @@ class Ha(object): return False # try to pick some other members for switchover and check that they are healthy - if failover.leader: + if failover.is_switchover: if self.state_handler.name == failover.leader: # I was the leader # exclude desired member which is unhealthy if it was specified if self.is_failover_possible(exclude_failover_candidate=bool(failover.candidate)): @@ -1101,7 +1101,7 @@ class Ha(object): if self.cluster.failover: # When doing a switchover in synchronous mode only synchronous nodes and former leader are allowed to race - if self.cluster.failover.leader and self.sync_mode_is_active() \ + if self.cluster.failover.is_switchover and self.sync_mode_is_active() \ and not self.cluster.sync.matches(self.state_handler.name, True): return False return self.manual_failover_process_no_leader() or False @@ -2022,7 +2022,7 @@ class Ha(object): def is_eligible(node: Member) -> bool: # in synchronous mode we allow failover (not switchover!) to async node if self.sync_mode_is_active() and not self.cluster.sync.matches(node.name)\ - and not (failover and not failover.leader): + and not (failover and failover.is_failover): return False # Don't spend time on "nofailover" nodes checking. # We also don't need nodes which we can't query with the api in the list. diff --git a/patroni/utils.py b/patroni/utils.py index 881a2245..4c816bbf 100644 --- a/patroni/utils.py +++ b/patroni/utils.py @@ -839,8 +839,9 @@ def cluster_as_json(cluster: 'Cluster', global_config: Optional['GlobalConfig'] ret['pause'] = True if cluster.failover and cluster.failover.scheduled_at: ret['scheduled_switchover'] = {'at': cluster.failover.scheduled_at.isoformat()} - if cluster.failover.leader: - ret['scheduled_switchover']['from'] = cluster.failover.leader + if TYPE_CHECKING: # pragma: no cover + assert cluster.failover.leader + ret['scheduled_switchover']['from'] = cluster.failover.leader if cluster.failover.candidate: ret['scheduled_switchover']['to'] = cluster.failover.candidate return ret