Skip to content

Commit

Permalink
Release v3.2.2 (patroni#3007)
Browse files Browse the repository at this point in the history
- update release notes
- bump Patroni version
- bump pyright version and fix reported issues
- improve compatibility with legacy psycopg2

Co-authored-by: Polina Bungina <[email protected]>
  • Loading branch information
CyberDem0n and hughcapet committed Jan 17, 2024
1 parent f2919f9 commit c8e3277
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ jobs:

- uses: jakebailey/pyright-action@v1
with:
version: 1.1.338
version: 1.1.347

docs:
runs-on: ubuntu-latest
Expand Down
50 changes: 50 additions & 0 deletions docs/releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,56 @@
Release notes
=============

Version 3.2.2
-------------

**Bugfixes**

- Don't let replica restore initialize key when DCS was wiped (Alexander Kukushkin)

It was happening in the method where Patroni was supposed to take over a standalone PG cluster.

- Use consistent read when fetching just updated sync key from Consul (Alexander Kukushkin)

Consul doesn't provide any interface to immediately get ``ModifyIndex`` for the key that we just updated, therefore we have to perform an explicit read operation. Since stale reads are allowed by default, we sometimes used to get an outdated version of the key.

- Reload Postgres config if a parameter that requires restart was reset to the original value (Polina Bungina)

Previously Patroni wasn't updating the config, but only resetting the ``pending_restart``.

- Fix erroneous inverted logic of the confirmation prompt message when doing a failover to an async candidate in synchronous mode (Polina Bungina)

The problem existed only in ``patronictl``.

- Exclude leader from failover candidates in ``patronictl`` (Polina Bungina)

If the cluster is healthy, failing over to an existing leader is no-op.

- Create Citus database and extension idempotently (Alexander Kukushkin, Zhao Junwang)

It will allow to create them in the ``post_bootstrap`` script in case if there is a need to add some more dependencies to the Citus database.

- Don't filter our contradictory ``nofailover`` tag (Polina Bungina)

The configuration ``{nofailover: false, failover_priority: 0}`` set on a node didn't allow it to participate in the race, while it should, because ``nofailover`` tag should take precedence.

- Fixed PyInstaller frozen issue (Sophia Ruan)

The ``freeze_support()`` was called after ``argparse`` and as a result, Patroni wasn't able to start Postgres.

- Fixed bug in the config generator for ``patronictl`` and ``Citus`` configuration (Israel Barth Rubio)

It prevented ``patronictl`` and ``Citus`` configuration parameters set via environment variables from being written into the generated config.

- Restore recovery GUCs and some Patroni-managed parameters when joining a running standby (Alexander Kukushkin)

Patroni was failing to restart Postgres v12 onwards with an error about missing ``port`` in one of the internal structures.

- Fixes around ``pending_restart`` flag (Polina Bungina)

Don't expose ``pending_restart`` when in custom bootstrap with ``recovery_target_action = promote`` or when someone changed ``hot_standby`` or ``wal_log_hints`` using for example ``ALTER SYSTEM``.


Version 3.2.1
-------------

Expand Down
4 changes: 2 additions & 2 deletions patroni/ctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1418,7 +1418,7 @@ def switchover(obj: Dict[str, Any], cluster_name: str, group: Optional[int],


def generate_topology(level: int, member: Dict[str, Any],
topology: Dict[str, List[Dict[str, Any]]]) -> Iterator[Dict[str, Any]]:
topology: Dict[Optional[str], List[Dict[str, Any]]]) -> Iterator[Dict[str, Any]]:
"""Recursively yield members with their names adjusted according to their *level* in the cluster topology.
.. note::
Expand Down Expand Up @@ -1481,7 +1481,7 @@ def topology_sort(members: List[Dict[str, Any]]) -> Iterator[Dict[str, Any]]:
:yields: *members* sorted by level in the topology, and with a new ``name`` value according to their level
in the topology.
"""
topology: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
topology: Dict[Optional[str], List[Dict[str, Any]]] = defaultdict(list)
leader = next((m for m in members if m['role'].endswith('leader')), {'name': None})
replicas = set(member['name'] for member in members if not member['role'].endswith('leader'))
for member in members:
Expand Down
9 changes: 6 additions & 3 deletions patroni/postgresql/citus.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Any, Dict, List, Optional, Union, Tuple, TYPE_CHECKING

from ..dcs import CITUS_COORDINATOR_GROUP_ID, Cluster
from ..psycopg import connect, quote_ident, DuplicateDatabase
from ..psycopg import connect, quote_ident, ProgrammingError

if TYPE_CHECKING: # pragma: no cover
from . import Postgresql
Expand Down Expand Up @@ -364,8 +364,11 @@ def bootstrap(self) -> None:
with conn.cursor() as cur:
cur.execute('CREATE DATABASE {0}'.format(
quote_ident(self._config['database'], conn)).encode('utf-8'))
except DuplicateDatabase as e:
logger.debug('Exception when creating database: %r', e)
except ProgrammingError as exc:
if exc.diag.sqlstate == '42P04': # DuplicateDatabase
logger.debug('Exception when creating database: %r', exc)
else:
raise exc
finally:
conn.close()

Expand Down
5 changes: 1 addition & 4 deletions patroni/psycopg.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
from psycopg import Connection
from psycopg2 import connection, cursor

__all__ = ['connect', 'quote_ident', 'quote_literal', 'DatabaseError', 'Error', 'OperationalError', 'ProgrammingError',
'DuplicateDatabase']
__all__ = ['connect', 'quote_ident', 'quote_literal', 'DatabaseError', 'Error', 'OperationalError', 'ProgrammingError']

_legacy = False
try:
Expand All @@ -19,7 +18,6 @@
if parse_version(__version__) < MIN_PSYCOPG2:
raise ImportError
from psycopg2 import connect as _connect, Error, DatabaseError, OperationalError, ProgrammingError
from psycopg2.errors import DuplicateDatabase
from psycopg2.extensions import adapt

try:
Expand All @@ -45,7 +43,6 @@ def quote_literal(value: Any, conn: Optional[Any] = None) -> str:
return value.getquoted().decode('utf-8')
except ImportError:
from psycopg import connect as __connect, sql, Error, DatabaseError, OperationalError, ProgrammingError
from psycopg.errors import DuplicateDatabase

def _connect(dsn: Optional[str] = None, **kwargs: Any) -> 'Connection[Any]':
"""Call :func:`psycopg.connect` with *dsn* and ``**kwargs``.
Expand Down
2 changes: 1 addition & 1 deletion patroni/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
:var __version__: the current Patroni version.
"""
__version__ = '3.2.1'
__version__ = '3.2.2'
2 changes: 0 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ def execute(self, sql, *params):
sql = sql.decode('utf-8')
if sql.startswith('blabla'):
raise psycopg.ProgrammingError()
if sql.startswith('CREATE DATABASE'):
raise psycopg.DuplicateDatabase()
elif sql == 'CHECKPOINT' or sql.startswith('SELECT pg_catalog.pg_create_'):
raise psycopg.OperationalError()
elif sql.startswith('RetryFailedError'):
Expand Down
10 changes: 8 additions & 2 deletions tests/test_citus.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import time
from mock import Mock, patch
from mock import Mock, patch, PropertyMock
from patroni.postgresql.citus import CitusHandler
from patroni.psycopg import ProgrammingError

from . import BaseTestPostgresql, MockCursor, psycopg_connect, SleepException
from .test_ha import get_cluster_initialized_with_leader
Expand Down Expand Up @@ -166,6 +167,11 @@ def test_ignore_replication_slot(self):
@patch('patroni.postgresql.citus.connect', psycopg_connect)
@patch('patroni.postgresql.citus.quote_ident', Mock())
def test_bootstrap_duplicate_database(self, mock_logger):
self.c.bootstrap()
with patch.object(MockCursor, 'execute', Mock(side_effect=ProgrammingError)):
self.assertRaises(ProgrammingError, self.c.bootstrap)
with patch.object(MockCursor, 'execute', Mock(side_effect=[ProgrammingError, None, None, None])), \
patch.object(ProgrammingError, 'diag') as mock_diag:
type(mock_diag).sqlstate = PropertyMock(return_value='42P04')
self.c.bootstrap()
mock_logger.assert_called_once()
self.assertTrue(mock_logger.call_args[0][0].startswith('Exception when creating database'))

0 comments on commit c8e3277

Please sign in to comment.