From 00d125c512bdf4ce49cf4ed4722a40a531d4399f Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Fri, 12 Nov 2021 15:00:54 +0100 Subject: [PATCH] Avoid unnecessary updates of the members ZNode. (#2115) When deciding whether the ZNode should be updated we rely on the cached version of the cluster, which is updated only when members ZNodes are deleted/created or the `/status`, `/sync`, `/failover`, `/config`, or `/history` ZNodes are updated. I.e. after the update of the current member ZNode succeeded the cache becomes stale and all further updates are always performed even if the value didn't change. In order to solve it, we introduce the new attribute in the Zookeeper class and will use it for memorizing the actual value and for later comparison. --- patroni/dcs/zookeeper.py | 14 +++++++++----- tests/test_zookeeper.py | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/patroni/dcs/zookeeper.py b/patroni/dcs/zookeeper.py index 9cbff65e..4ac03cad 100644 --- a/patroni/dcs/zookeeper.py +++ b/patroni/dcs/zookeeper.py @@ -105,6 +105,7 @@ class ZooKeeper(AbstractDCS): self._fetch_cluster = True self._fetch_status = True + self.__last_member_data = None self._orig_kazoo_connect = self._client._connection._connect self._client._connection._connect = self._kazoo_connect @@ -348,11 +349,11 @@ class ZooKeeper(AbstractDCS): def touch_member(self, data, permanent=False): cluster = self.cluster member = cluster and cluster.get_member(self._name, fallback_to_leader=False) - encoded_data = json.dumps(data, separators=(',', ':')).encode('utf-8') + member_data = self.__last_member_data or member and member.data if member and (self._client.client_id is not None and member.session != self._client.client_id[0] or - not (deep_compare(member.data.get('tags', {}), data.get('tags', {})) and - member.data.get('version') == data.get('version') and - member.data.get('checkpoint_after_promote') == data.get('checkpoint_after_promote'))): + not (deep_compare(member_data.get('tags', {}), data.get('tags', {})) and + member_data.get('version') == data.get('version') and + member_data.get('checkpoint_after_promote') == data.get('checkpoint_after_promote'))): try: self._client.delete_async(self.member_path).get(timeout=1) except NoNodeError: @@ -361,13 +362,15 @@ class ZooKeeper(AbstractDCS): return False member = None + encoded_data = json.dumps(data, separators=(',', ':')).encode('utf-8') if member: - if deep_compare(data, member.data): + if deep_compare(data, member_data): return True else: try: self._client.create_async(self.member_path, encoded_data, makepath=True, ephemeral=not permanent).get(timeout=1) + self.__last_member_data = data return True except Exception as e: if not isinstance(e, NodeExistsError): @@ -375,6 +378,7 @@ class ZooKeeper(AbstractDCS): return False try: self._client.set_async(self.member_path, encoded_data).get(timeout=1) + self.__last_member_data = data return True except Exception: logger.exception('touch_member') diff --git a/tests/test_zookeeper.py b/tests/test_zookeeper.py index ed718a21..09857fb2 100644 --- a/tests/test_zookeeper.py +++ b/tests/test_zookeeper.py @@ -213,6 +213,7 @@ class TestZooKeeper(unittest.TestCase): self.zk.touch_member({'retry': 'retry'}) self.zk._fetch_cluster = True self.zk.get_cluster() + self.zk.touch_member({'retry': 'retry'}) self.zk.touch_member({'conn_url': 'postgres://repuser:rep-pass@localhost:5434/postgres', 'api_url': 'http://127.0.0.1:8009/patroni'})