mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-08-25 14:53:37 +00:00
Release v3.3.1 (#3087)
* Update release notes * Bump version * Bump pyright version and solve reported issues --------- Co-authored-by: Alexander Kukushkin <[email protected]>
This commit is contained in:
co-authored by
Alexander Kukushkin
parent
d4fd782038
commit
6b7ec49282
@@ -186,7 +186,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: jakebailey/pyright-action@v2
|
- uses: jakebailey/pyright-action@v2
|
||||||
with:
|
with:
|
||||||
version: 1.1.356
|
version: 1.1.367
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -3,6 +3,33 @@
|
|||||||
Release notes
|
Release notes
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
Version 3.3.1
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Released 2024-06-17
|
||||||
|
|
||||||
|
**Stability improvements**
|
||||||
|
|
||||||
|
- Compatibility with Python 3.12 (Alexander Kukushkin)
|
||||||
|
|
||||||
|
Handle a new attribute added to ``logging.LogRecord``.
|
||||||
|
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
- Fix infinite recursion in ``replicatefrom`` tags handling (Alexander Kukushkin)
|
||||||
|
|
||||||
|
As a part of this fix, also improve ``is_physical_slot()`` check and adjust documentation.
|
||||||
|
|
||||||
|
- Fix wrong role reporting in standby clusters (Alexander Kukushkin)
|
||||||
|
|
||||||
|
`synchronous_standby_names` and synchronous replication only work on a real primary node and in the case of cascading replication are simply ignored by Postgres. Before this fix, `patronictl list` and `GET /cluster` were falsely reporting some nodes as synchronous.
|
||||||
|
|
||||||
|
- Fix availability of the ``allow_in_place_tablespaces`` GUC (Polina Bungina)
|
||||||
|
|
||||||
|
``allow_in_place_tablespaces`` was not only added to PostgreSQL 15 but also backpatched to PostgreSQL 10-14.
|
||||||
|
|
||||||
|
|
||||||
Version 3.3.0
|
Version 3.3.0
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|||||||
+16
-6
@@ -62,6 +62,15 @@ def error_exception(self: logging.Logger, msg: object, *args: Any, **kwargs: Any
|
|||||||
self.error(msg, *args, exc_info=exc_info, **kwargs)
|
self.error(msg, *args, exc_info=exc_info, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _type(value: Any) -> str:
|
||||||
|
"""Get type of the *value*.
|
||||||
|
|
||||||
|
:param value: any arbitrary value.
|
||||||
|
:returns: a string with a type name.
|
||||||
|
"""
|
||||||
|
return value.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
class QueueHandler(logging.Handler):
|
class QueueHandler(logging.Handler):
|
||||||
"""Queue-based logging handler.
|
"""Queue-based logging handler.
|
||||||
|
|
||||||
@@ -292,7 +301,7 @@ class PatroniLogger(Thread):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not isinstance(logformat, str):
|
if not isinstance(logformat, str):
|
||||||
_LOGGER.warning('Expected log format to be a string when log type is plain, but got "%s"', type(logformat))
|
_LOGGER.warning('Expected log format to be a string when log type is plain, but got "%s"', _type(logformat))
|
||||||
logformat = PatroniLogger.DEFAULT_FORMAT
|
logformat = PatroniLogger.DEFAULT_FORMAT
|
||||||
|
|
||||||
return logging.Formatter(logformat, dateformat)
|
return logging.Formatter(logformat, dateformat)
|
||||||
@@ -330,13 +339,13 @@ class PatroniLogger(Thread):
|
|||||||
else:
|
else:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
'Expected renamed log field to be a string, but got "%s"',
|
'Expected renamed log field to be a string, but got "%s"',
|
||||||
type(renamed_field)
|
_type(renamed_field)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
'Expected each item of log format to be a string or dictionary, but got "%s"',
|
'Expected each item of log format to be a string or dictionary, but got "%s"',
|
||||||
type(field)
|
_type(field)
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(log_fields) > 0:
|
if len(log_fields) > 0:
|
||||||
@@ -346,11 +355,12 @@ class PatroniLogger(Thread):
|
|||||||
else:
|
else:
|
||||||
jsonformat = PatroniLogger.DEFAULT_FORMAT
|
jsonformat = PatroniLogger.DEFAULT_FORMAT
|
||||||
rename_fields = {}
|
rename_fields = {}
|
||||||
_LOGGER.warning('Expected log format to be a string or a list, but got "%s"', type(logformat))
|
_LOGGER.warning('Expected log format to be a string or a list, but got "%s"', _type(logformat))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pythonjsonlogger import jsonlogger
|
from pythonjsonlogger import jsonlogger
|
||||||
if hasattr(jsonlogger, 'RESERVED_ATTRS') and 'taskName' not in jsonlogger.RESERVED_ATTRS:
|
if hasattr(jsonlogger, 'RESERVED_ATTRS') \
|
||||||
|
and 'taskName' not in jsonlogger.RESERVED_ATTRS: # pyright: ignore [reportUnnecessaryContains]
|
||||||
# compatibility with python 3.12, that added a new attribute to LogRecord
|
# compatibility with python 3.12, that added a new attribute to LogRecord
|
||||||
jsonlogger.RESERVED_ATTRS += ('taskName',)
|
jsonlogger.RESERVED_ATTRS += ('taskName',)
|
||||||
|
|
||||||
@@ -380,7 +390,7 @@ class PatroniLogger(Thread):
|
|||||||
static_fields = config.get('static_fields', {})
|
static_fields = config.get('static_fields', {})
|
||||||
|
|
||||||
if dateformat is not None and not isinstance(dateformat, str):
|
if dateformat is not None and not isinstance(dateformat, str):
|
||||||
_LOGGER.warning('Expected log dateformat to be a string, but got "%s"', type(dateformat))
|
_LOGGER.warning('Expected log dateformat to be a string, but got "%s"', _type(dateformat))
|
||||||
dateformat = None
|
dateformat = None
|
||||||
|
|
||||||
if logtype == 'json':
|
if logtype == 'json':
|
||||||
|
|||||||
@@ -893,7 +893,8 @@ class ConfigHandler(object):
|
|||||||
return re.sub(r'([:\\])', r'\\\1', str(value))
|
return re.sub(r'([:\\])', r'\\\1', str(value))
|
||||||
|
|
||||||
# 'host' could be several comma-separated hostnames, in this case we need to write on pgpass line per host
|
# 'host' could be several comma-separated hostnames, in this case we need to write on pgpass line per host
|
||||||
hosts = map(escape, filter(None, map(str.strip, (record.get('host') or '*').split(','))))
|
hosts = map(escape, filter(None, map(str.strip,
|
||||||
|
(record.get('host', '') or '*').split(',')))) # pyright: ignore [reportUnknownArgumentType]
|
||||||
record = {n: escape(record.get(n) or '*') for n in ('port', 'user', 'password')}
|
record = {n: escape(record.get(n) or '*') for n in ('port', 'user', 'password')}
|
||||||
return '\n'.join('{host}:{port}:*:{user}:{password}'.format(**record, host=host) for host in hosts)
|
return '\n'.join('{host}:{port}:*:{user}:{password}'.format(**record, host=host) for host in hosts)
|
||||||
|
|
||||||
|
|||||||
@@ -428,7 +428,8 @@ class CitusHandler(Citus, AbstractMPPHandler, Thread):
|
|||||||
# citus extension must be on the first place in shared_preload_libraries
|
# citus extension must be on the first place in shared_preload_libraries
|
||||||
shared_preload_libraries = list(filter(
|
shared_preload_libraries = list(filter(
|
||||||
lambda el: el and el != 'citus',
|
lambda el: el and el != 'citus',
|
||||||
[p.strip() for p in parameters.get('shared_preload_libraries', '').split(',')]))
|
map(str.strip, parameters.get('shared_preload_libraries', '').split(',')))
|
||||||
|
) # pyright: ignore [reportUnknownArgumentType]
|
||||||
parameters['shared_preload_libraries'] = ','.join(['citus'] + shared_preload_libraries)
|
parameters['shared_preload_libraries'] = ','.join(['citus'] + shared_preload_libraries)
|
||||||
|
|
||||||
# if not explicitly set Citus overrides max_prepared_transactions to max_connections*2
|
# if not explicitly set Citus overrides max_prepared_transactions to max_connections*2
|
||||||
|
|||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
:var __version__: the current Patroni version.
|
:var __version__: the current Patroni version.
|
||||||
"""
|
"""
|
||||||
__version__ = '3.3.0'
|
__version__ = '3.3.1'
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
"reportMissingImports": true,
|
"reportMissingImports": true,
|
||||||
"reportMissingTypeStubs": false,
|
"reportMissingTypeStubs": false,
|
||||||
|
|
||||||
"pythonVersion": "3.11",
|
"pythonVersion": "3.12",
|
||||||
"pythonPlatform": "All",
|
"pythonPlatform": "All",
|
||||||
|
|
||||||
"typeCheckingMode": "strict"
|
"typeCheckingMode": "strict"
|
||||||
|
|||||||
+1
-1
@@ -187,7 +187,7 @@ class TestPatroniLogger(unittest.TestCase):
|
|||||||
self.assertEqual(captured_log_level, 'WARNING')
|
self.assertEqual(captured_log_level, 'WARNING')
|
||||||
self.assertRegex(
|
self.assertRegex(
|
||||||
captured_log_message,
|
captured_log_message,
|
||||||
fr'Expected log dateformat to be a string, but got "{type(config["dateformat"])}"'
|
r'Expected log dateformat to be a string, but got "int"'
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_plain_format(self):
|
def test_invalid_plain_format(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user