From 535dc631ec96ea04d19cf7f4d736b27c068b2c0a Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Tue, 10 Oct 2023 12:21:19 +0200 Subject: [PATCH] Bugfix: standby cluster switchover (#2900) 1. Enforce `_load_cluster()` after acquisition for the leader lock in ZooKeeper. Sometimes the notification from ZooKeeper was arriving too late and Patroni wasn't setting the `role=standby_leader`. 2. The `_get_node_to_follow()` method was falsely assuming that we still own the leader lock and returning the remote node instead of the new standby leader. While not a big issue per se, because the next HA loop usually fixes it, such behavior was causing flakiness of behave tests with Postgres 12 and older, where restart is required to update `primary_conninfo` GUC. --- patroni/dcs/zookeeper.py | 1 + patroni/ha.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/patroni/dcs/zookeeper.py b/patroni/dcs/zookeeper.py index 863d7b40..649c1ac5 100644 --- a/patroni/dcs/zookeeper.py +++ b/patroni/dcs/zookeeper.py @@ -334,6 +334,7 @@ class ZooKeeper(AbstractDCS): try: self._client.retry(self._client.create, self.leader_path, self._name.encode('utf-8'), makepath=True, ephemeral=True) + self.cluster_watcher(None) # the next _load_cluster() call must read from ZooKeeper. return True except (ConnectionClosedError, RetryFailedError) as e: raise ZooKeeperError(e) diff --git a/patroni/ha.py b/patroni/ha.py index 3b936e4b..877e24ef 100644 --- a/patroni/ha.py +++ b/patroni/ha.py @@ -584,7 +584,9 @@ class Ha(object): """ # The standby leader or when there is no standby leader we want to follow # the remote member, except when there is no standby leader in pause. - if self.is_standby_cluster() and (self.has_lock(False) or self.cluster.is_unlocked() and not self.is_paused()): + if self.is_standby_cluster() \ + and (cluster.leader and cluster.leader.name and cluster.leader.name == self.state_handler.name + or cluster.is_unlocked() and not self.is_paused()): node_to_follow = self.get_remote_member() # If replicatefrom tag is set, try to follow the node mentioned there, otherwise, follow the leader. elif self.patroni.replicatefrom and self.patroni.replicatefrom != self.state_handler.name: