From 9b237b332e5d8770002a4b5152764d68a271f412 Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Thu, 28 Mar 2024 08:15:58 +0100 Subject: [PATCH] Set global_config from dynamic_config if DCS data is empty (#3038) Fix the oversight of 193c73f We need to set global config from the local cache if cluster.config is not initialized. If there is nothing written into the DCS (yet), we need the setup info for the decision making (e.g., if it is a standby cluster) --- patroni/global_config.py | 7 ++++--- patroni/ha.py | 3 +++ tests/test_ha.py | 6 ------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/patroni/global_config.py b/patroni/global_config.py index 7731cf59..ded66f6f 100644 --- a/patroni/global_config.py +++ b/patroni/global_config.py @@ -44,19 +44,20 @@ class GlobalConfig(types.ModuleType): """ return bool(cluster and cluster.config and cluster.config.modify_version) - def update(self, cluster: Optional['Cluster']) -> None: + def update(self, cluster: Optional['Cluster'], default: Optional[Dict[str, Any]] = None) -> None: """Update with the new global configuration from the :class:`Cluster` object view. .. note:: - Global configuration is updated only when configuration in the *cluster* view is valid. - Update happens in-place and is executed only from the main heartbeat thread. :param cluster: the currently known cluster state from DCS. + :param default: default configuration, which will be used if there is no valid *cluster.config*. """ # Try to protect from the case when DCS was wiped out if self._cluster_has_valid_config(cluster): self.__config = cluster.config.data # pyright: ignore [reportOptionalMemberAccess] + elif default: + self.__config = default def from_cluster(self, cluster: Optional['Cluster']) -> 'GlobalConfig': """Return :class:`GlobalConfig` instance from the provided :class:`Cluster` object view. diff --git a/patroni/ha.py b/patroni/ha.py index 16c050f3..59b5b6dd 100644 --- a/patroni/ha.py +++ b/patroni/ha.py @@ -185,6 +185,9 @@ class Ha(object): # used only in backoff after failing a pre_promote script self._released_leader_key_timestamp = 0 + # Initialize global config + global_config.update(None, self.patroni.config.dynamic_configuration) + def primary_stop_timeout(self) -> Union[int, None]: """:returns: "primary_stop_timeout" from the global configuration or `None` when not in synchronous mode.""" ret = global_config.primary_stop_timeout diff --git a/tests/test_ha.py b/tests/test_ha.py index 43844c5d..abfb4691 100644 --- a/tests/test_ha.py +++ b/tests/test_ha.py @@ -256,8 +256,6 @@ class TestHa(PostgresInit): self.p.data_directory_empty = true self.ha.cluster = get_cluster_not_initialized_without_leader( cluster_config=ClusterConfig(1, {"standby_cluster": {"port": 5432}}, 1)) - global_config.update(self.ha.cluster) - self.ha.cluster = get_cluster_not_initialized_without_leader(cluster_config=ClusterConfig(0, {}, 0)) self.assertEqual(self.ha.run_cycle(), 'trying to bootstrap a new standby leader') def test_bootstrap_waiting_for_standby_leader(self): @@ -323,7 +321,6 @@ class TestHa(PostgresInit): self.ha.state_handler.cancellable._process = Mock() self.ha._crash_recovery_started -= 600 self.ha.cluster.config.data.update({'maximum_lag_on_failover': 10}) - global_config.update(self.ha.cluster) self.assertEqual(self.ha.run_cycle(), 'terminated crash recovery because of startup timeout') @patch.object(Rewind, 'ensure_clean_shutdown', Mock()) @@ -776,7 +773,6 @@ class TestHa(PostgresInit): with patch('patroni.ha.logger.info') as mock_info: self.ha.fetch_node_status = get_node_status(wal_position=1) self.ha.cluster.config.data.update({'maximum_lag_on_failover': 5}) - global_config.update(self.ha.cluster) self.assertEqual(self.ha.run_cycle(), 'no action. I am (postgresql0), the leader with the lock') self.assertEqual(mock_info.call_args_list[0][0], ('Member %s exceeds maximum replication lag', 'leader')) @@ -1282,7 +1278,6 @@ class TestHa(PostgresInit): self.p.is_running = false self.ha.cluster = get_cluster_initialized_with_leader(sync=(self.p.name, 'other')) self.ha.cluster.config.data.update({'synchronous_mode': True, 'primary_start_timeout': 0}) - global_config.update(self.ha.cluster) self.ha.has_lock = true self.ha.update_lock = true self.ha.fetch_node_status = get_node_status() # accessible, in_recovery @@ -1391,7 +1386,6 @@ class TestHa(PostgresInit): mock_set_sync.reset_mock() self.p.sync_handler.current_state = Mock(return_value=(CaseInsensitiveSet(), CaseInsensitiveSet())) self.ha.cluster.config.data['synchronous_mode_strict'] = True - global_config.update(self.ha.cluster) self.ha.run_cycle() mock_set_sync.assert_called_once_with(CaseInsensitiveSet('*'))