Skip to content

Commit 1c3257a

Browse files
authored
Release v4.0.6 (patroni#3376)
* Pyright 1.1.401 * Release v4.0.6 * Apply suggestions from code review * Increase test coverage for postgresql/config.py
1 parent 61951bf commit 1c3257a

File tree

6 files changed

+82
-9
lines changed

6 files changed

+82
-9
lines changed

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ jobs:
198198

199199
- uses: jakebailey/pyright-action@v2
200200
with:
201-
version: 1.1.394
201+
version: 1.1.401
202202

203203
ydiff:
204204
name: Test compatibility with the latest version of ydiff

docs/releases.rst

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,66 @@
33
Release notes
44
=============
55

6+
Version 4.0.6
7+
-------------
8+
9+
Released 2025-06-06
10+
11+
**Bugfixes**
12+
13+
- Fix bug in failover from a leader with a higher priority (Alexander Kukushkin)
14+
15+
Make sure Patroni ignores the former leader with higher priority when it reports the same ``LSN`` as the current node.
16+
17+
- Fix permissions for the ``postgresql.conf`` file created outside of ``PGDATA`` (Michael Banck)
18+
19+
Respect the system-wide umask value when creating the ``postgresql.conf`` file outside of the ``PGDATA`` directory.
20+
21+
- Fix bug with switchover in ``synchronous_mode=quorum`` (Alexander Kukushkin)
22+
23+
Do not check quorum requirements when a candidate is specified.
24+
25+
- Ignore stale Etcd nodes by comparing cluster term (Alexander Kukushkin)
26+
27+
Memorize the last known "raft_term" of the Etcd cluster, and when executing client requests, compare it with the "raft_term" reported by an Etcd node.
28+
29+
- Update PostgreSQL configuration files on ``SIGHUP`` (Alexander Kukushkin)
30+
31+
Previously, Patroni was only replacing PostgreSQL configuration files if a change in global or local configuration was detected.
32+
33+
- Properly handle ``Unavailable`` exception raised by ``etcd3`` (Alexander Kukushkin)
34+
35+
Patroni used to retry such requests on the same ``etcd3`` node, while switching to another node is a better strategy.
36+
37+
- Improve ``etcd3`` lease handling (Alexander Kukushkin)
38+
39+
Make sure Patroni refreshes the ``etcd3`` lease at least once per HA loop.
40+
41+
- Recheck annotations on 409 status code when attempting to acquire leader lock (Alexander Kukushkin)
42+
43+
Implement the same behavior as was done for the leader object read in Patroni version 4.0.3.
44+
45+
- Consider ``replay_lsn`` when advancing slots (Polina Bungina)
46+
47+
Do not try to advance slots on replicas past the ``replay_lsn``. Additionally, advance the slot to the ``replay_lsn`` position if it is already past the ``confirmed_flush_lsn`` of this slot on the replica but the replica has still not replayed the actual ``LSN`` at which this slot is on the primary.
48+
49+
- Make sure ``CHECKPOINT`` is executed after promote (Alexander Kukushkin)
50+
51+
It was possible that checkpoint task wasn't reset on demote because ``CHECKPOINT`` wasn't yet finished. This resulted in using a stale ``result`` when the next promote is triggered.
52+
53+
- Avoid running "offline" demotion concurrently (Alexander Kukushkin)
54+
55+
In case of a slow shutdown, it might happen that the next heartbeat loop hits the DCS error handling method again, resulting in ``AsyncExecutor is busy, demoting from the main thread`` warning and starting offline demotion again.
56+
57+
- Normalize the ``data_dir`` value before renaming the data directory on initialization failure (Waynerv)
58+
59+
Prevent a trailing slash in the ``data_dir`` parameter value from breaking the renaming process after an initialization failure.
60+
61+
- Check that ``synchronous_standby_names`` contains the expected value (Alexander Kukushkin)
62+
63+
Previously, the mechanism implementing the state machine for non-quorum synchronous replication didn't check the actual value of ``synchronous_standby_names``, what resulted in a stale value of ``synchronous_standby_names`` being used when ``pg_stat_replication`` is a subset of ``synchronous_standby_names``.
64+
65+
666
Version 4.0.5
767
-------------
868

patroni/dcs/kubernetes.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from copy import deepcopy
1515
from http.client import HTTPException
1616
from threading import Condition, Lock, Thread
17-
from typing import Any, Callable, Collection, Dict, List, Optional, Tuple, Type, TYPE_CHECKING, Union
17+
from typing import Any, Callable, cast, Collection, Dict, List, Optional, Tuple, Type, TYPE_CHECKING, Union
1818

1919
import urllib3
2020
import yaml
@@ -190,13 +190,12 @@ def __getattr__(self, name: str) -> Any:
190190
@classmethod
191191
def _wrap(cls, parent: Optional[str], value: Any) -> Any:
192192
if isinstance(value, dict):
193-
data_dict: Dict[str, Any] = value
193+
data_dict: Dict[str, Any] = cast(Dict[str, Any], value)
194194
# we know that `annotations` and `labels` are dicts and therefore don't want to convert them into K8sObject
195195
return data_dict if parent in {'annotations', 'labels'} and \
196196
all(isinstance(v, str) for v in data_dict.values()) else cls(data_dict)
197197
elif isinstance(value, list):
198-
data_list: List[Any] = value
199-
return [cls._wrap(None, v) for v in data_list]
198+
return [cls._wrap(None, v) for v in cast(List[Any], value)]
200199
else:
201200
return value
202201

patroni/postgresql/validator.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33

44
from copy import deepcopy
5-
from typing import Any, Dict, Iterator, List, MutableMapping, Optional, Tuple, Type, Union
5+
from typing import Any, cast, Dict, Iterator, List, MutableMapping, Optional, Tuple, Type, Union
66

77
import yaml
88

@@ -223,8 +223,7 @@ def __new__(cls, validator: Dict[str, Any]) -> _Transformable:
223223
for key, value in validator.items():
224224
# :func:`_transform_parameter_value` expects :class:`tuple` instead of :class:`list`
225225
if isinstance(value, list):
226-
tmp_value: List[Any] = value
227-
validator[key] = tuple(tmp_value)
226+
validator[key] = tuple(cast(List[Any], value))
228227

229228
try:
230229
return cls.TYPES[type_](**validator)

patroni/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
33
:var __version__: the current Patroni version.
44
"""
5-
__version__ = '4.0.5'
5+
__version__ = '4.0.6'

tests/test_postgresql.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import datetime
22
import os
33
import re
4+
import stat
45
import subprocess
56
import time
67

@@ -18,6 +19,7 @@
1819
from patroni.collections import CaseInsensitiveDict, CaseInsensitiveSet
1920
from patroni.dcs import RemoteMember
2021
from patroni.exceptions import PatroniException, PostgresConnectionException
22+
from patroni.file_perm import pg_perm
2123
from patroni.postgresql import PgIsReadyStatus, Postgresql
2224
from patroni.postgresql.bootstrap import Bootstrap
2325
from patroni.postgresql.callback_executor import CallbackAction
@@ -1147,6 +1149,19 @@ def test__load_postgres_gucs_validators(self):
11471149
"directory:", mock_warning.call_args[0][0]
11481150
)
11491151

1152+
@patch('os.chmod')
1153+
@patch('os.stat')
1154+
@patch('os.umask')
1155+
def test_set_file_permissions(self, mock_umask, mock_stat, mock_chmod):
1156+
pg_conf = os.path.join(self.p.data_dir, 'postgresql.conf')
1157+
mock_stat.return_value.st_mode = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP # MODE_GROUP
1158+
self.p.config.set_file_permissions(pg_conf)
1159+
mock_chmod.assert_called_with(pg_conf, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
1160+
1161+
pg_conf = os.path.join(os.path.abspath(os.sep) + 'tmp', 'postgresql.conf')
1162+
self.p.config.set_file_permissions(pg_conf)
1163+
mock_chmod.assert_called_with(pg_conf, 0o666 & ~pg_perm.orig_umask)
1164+
11501165

11511166
@patch('subprocess.call', Mock(return_value=0))
11521167
@patch('patroni.psycopg.connect', psycopg_connect)

0 commit comments

Comments
 (0)