Skip to content

Commit

Permalink
Set global_config from dynamic_config if DCS data is empty (patroni#3038
Browse files Browse the repository at this point in the history
)

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)
  • Loading branch information
hughcapet authored Mar 28, 2024
1 parent b09af64 commit 9b237b3
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 9 deletions.
7 changes: 4 additions & 3 deletions patroni/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,20 @@ def _cluster_has_valid_config(cluster: Optional['Cluster']) -> bool:
"""
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.
Expand Down
3 changes: 3 additions & 0 deletions patroni/ha.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ def __init__(self, patroni: Patroni):
# 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
Expand Down
6 changes: 0 additions & 6 deletions tests/test_ha.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,6 @@ def test_bootstrap_as_standby_leader(self, initialize):
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):
Expand Down Expand Up @@ -323,7 +321,6 @@ def test_crash_recovery(self):
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())
Expand Down Expand Up @@ -776,7 +773,6 @@ def test_manual_switchover_from_leader(self):
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'))

Expand Down Expand Up @@ -1282,7 +1278,6 @@ def test_failover_immediately_on_zero_primary_start_timeout(self, demote):
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
Expand Down Expand Up @@ -1391,7 +1386,6 @@ def test_process_sync_replication(self):
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('*'))

Expand Down

0 comments on commit 9b237b3

Please sign in to comment.