Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3882c64
Switch to codecov (#3451)
CyberDem0n Sep 26, 2025
c33d3ef
Correct the description of watchdog logging change (#3456)
bkhomuts Oct 2, 2025
e1eed26
Fix issue with renaming leader node in sync mode and pause (#3454)
CyberDem0n Oct 2, 2025
b814b75
Fix typo in docs/installation.rst (#3458)
CyberDem0n Oct 3, 2025
8de33b0
Trigger pg_rewind check when the same primary increased timeline (#3457)
CyberDem0n Oct 3, 2025
c46e4a6
Docs: fix link to replication_modes (#3471)
gmelikov Oct 20, 2025
730636e
Only write superuser password during initdb bootstrap if it is non-em…
mbanck-cd Oct 20, 2025
350d090
There is no member-name for edit-config (#3476)
krisavi Oct 23, 2025
b867f76
Adjust NotifyAccess (#3485)
otbutz Nov 11, 2025
c3b6b1c
Compatibility with Python 3.14 (#3465)
CyberDem0n Nov 27, 2025
2fd4938
Fix bug with failover_priority with synchronous_mode=on (#3498)
CyberDem0n Nov 27, 2025
34c1e5b
A couple improvements for Etcd3 error handling (#3486)
CyberDem0n Nov 28, 2025
f3f9c60
Make Make unit tests deterministic with python < 3.8 (#3504)
CyberDem0n Nov 28, 2025
43954ea
Don't start stopped postgres on replica with nofailover tag (#3501)
CyberDem0n Dec 1, 2025
f563610
Fix bug with primary_conninfo password comparison (#3507)
CyberDem0n Dec 22, 2025
32f6dee
Fix check_recovery_conf() when PostgreSQL is in the starting state (#…
CyberDem0n Dec 29, 2025
f8c5ddc
Fix link for `slots` configuration in existing_data.rst (#3528)
zaneduffield Jan 21, 2026
6945d42
Fix typo in replication modes documentation (#3535)
waynerv Jan 29, 2026
e5a91b3
Logo (update) (#3531)
mbanck-cd Jan 29, 2026
9d260fe
Unpin sphinx-github-style and sphinx versions (#3554)
CyberDem0n Mar 5, 2026
8fdc302
Fix nondeterministic failures of GET_readiness tests (#3553)
CyberDem0n Mar 5, 2026
e5a4354
Fix: validate user options in dict format for initdb/basebackup (#3537)
m4rrypro Mar 5, 2026
aaf17be
Allow server-side compression for basebackup option (#3538)
m4rrypro Mar 16, 2026
422c3ba
standby cluster warning for member name collision with primary cluste…
kavithaa0201 Mar 17, 2026
2b57136
Properly handle Unavailable exception raised by Etcd v3 (#3562)
CyberDem0n Mar 17, 2026
66cacdb
Don't reload PostgreSQL config while running custom bootstrap (#3563)
CyberDem0n Mar 17, 2026
617a033
Compatibility with threading changes in python3.11+ (#3526)
CyberDem0n Mar 18, 2026
d780892
Document heartbeat log deduplication in FAQ (#3565)
m4rrypro Mar 19, 2026
7cb2686
Check that postgresql.parameters is dict (#3568)
CyberDem0n Mar 19, 2026
57864d9
Shutdown global thread pool last (#3569)
CyberDem0n Mar 19, 2026
0149db3
Don't hit time.sleep() in unit tests (#3570)
CyberDem0n Mar 20, 2026
513f8e9
Postpone CitusHandler.run() (#3571)
CyberDem0n Mar 20, 2026
2c269b3
add retry for 403 Permission denied when update leader (#3559)
XiuhuaRuan Mar 26, 2026
8299050
Compatibility with latest Etcd (#3580)
CyberDem0n Apr 8, 2026
0a045e0
Don't hit time.sleep() in unit tests (#3583)
CyberDem0n Apr 8, 2026
a79f35a
Release 4.1.1 (#3574)
hughcapet Apr 8, 2026
7ef3cd4
Add support for notify-reload systemd unit type (#3450)
rdunklau Oct 5, 2025
59b2173
Do not let PostgreSQL to notify systemd (#3590)
CyberDem0n Apr 21, 2026
23ed5e0
Merge tag 'v4.1.2' into multisite-v4.1.2
ants Apr 30, 2026
70f3732
Fix cluster status query on 9.6
ants Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/mapping.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
versions = {'etcd': '9.6', 'etcd3': '16', 'consul': '17', 'exhibitor': '12', 'raft': '14', 'kubernetes': '15'}
versions = {'etcd': '9.6', 'etcd3': '17', 'consul': '18', 'exhibitor': '12', 'raft': '14', 'kubernetes': '16'}
58 changes: 28 additions & 30 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
os: [ubuntu, windows, macos]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.7
uses: actions/setup-python@v5
Expand Down Expand Up @@ -91,18 +91,25 @@ jobs:
run: python .github/workflows/run_tests.py
if: matrix.os != 'macos'

- name: Set up Python 3.14
uses: actions/setup-python@v5
with:
python-version: 3.14
if: matrix.os != 'macos'
- name: Install dependencies
run: python .github/workflows/install_deps.py
if: matrix.os != 'macos'
- name: Run tests and flake8
run: python .github/workflows/run_tests.py
if: matrix.os != 'macos'

- name: Combine coverage
run: python .github/workflows/run_tests.py combine

- name: Install coveralls
run: python -m pip install coveralls

- name: Upload Coverage
env:
COVERALLS_FLAG_NAME: unit-${{ matrix.os }}
COVERALLS_PARALLEL: 'true'
GITHUB_TOKEN: ${{ secrets.github_token }}
run: python -m coveralls --service=github
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
flags: unit-${{ matrix.os }}

behave:
runs-on: ${{ fromJson('{"ubuntu":"ubuntu-22.04","windows":"windows-latest","macos":"macos-14"}')[matrix.os] }}
Expand All @@ -114,7 +121,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu]
python-version: [3.7, 3.13]
python-version: [3.7, 3.14]
dcs: [etcd, etcd3, consul, exhibitor, kubernetes, raft]
include:
- os: macos
Expand All @@ -128,12 +135,14 @@ jobs:
dcs: etcd3

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: nolar/setup-k3d-k3s@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
if: matrix.dcs == 'kubernetes'
- name: Add postgresql and citus apt repo
run: |
Expand Down Expand Up @@ -164,17 +173,6 @@ jobs:
run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r cobertura.xml -l Python --partial
if: ${{ env.SECRETS_AVAILABLE == 'true' }}

coveralls-finish:
name: Finalize coveralls.io
needs: unit
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v5
- run: python -m pip install coveralls
- run: python -m coveralls --service=github --finish
env:
GITHUB_TOKEN: ${{ secrets.github_token }}

codacy-final:
name: Finalize Codacy
needs: behave
Expand All @@ -186,25 +184,25 @@ jobs:
pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.13
- name: Set up Python 3.14
uses: actions/setup-python@v5
with:
python-version: 3.13
python-version: 3.14

- name: Install dependencies
run: python -m pip install -r requirements.txt psycopg2-binary psycopg

- uses: jakebailey/pyright-action@v2
with:
version: 1.1.405
version: 1.1.408

ydiff:
name: Test compatibility with the latest version of ydiff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.13
uses: actions/setup-python@v5
Expand All @@ -220,7 +218,7 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.11
uses: actions/setup-python@v5
Expand All @@ -244,7 +242,7 @@ jobs:
isort:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up Python 3.12
uses: actions/setup-python@v5
Expand Down
49 changes: 47 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,55 @@
|Tests Status| |Coverage Status|

.. image:: docs/_static/patroni-logo.svg
:height: 128px
:width: 128px

Patroni: A Template for PostgreSQL HA with ZooKeeper, etcd or Consul
--------------------------------------------------------------------

You can find a version of this documentation that is searchable and also easier to navigate at `patroni.readthedocs.io <https://patroni.readthedocs.io>`__.

**Important!**
Running Patroni on **memory-restricted systems with Python 3.11+**

----

If you run Patroni on a system with strict memory limits, for example with ``vm.overcommit_memory=2`` (recommended for PostgreSQL), and use Python 3.11 or newer, you may observe unexpected behavior:

- Patroni appears healthy
- PostgreSQL continues to run
- Patroni **REST API becomes unresponsive**
- The operating system reports that Patroni is listening on the REST API port
- Patroni logs look normal; however, following messages may appear once: ``Exception ignored in thread started by: <object repr() failed>``, ``MemoryError``
- Kernel logs may contain messages such as ``not enough memory for the allocation``

This behavior is caused by a `bug in Python 3.11+ <https://github.com/python/cpython/issues/140746>`__.
Under strict memory conditions, starting a new thread may hang indefinitely when there is not enough free memory.

Recommended solution
--------------------

Recent Patroni releases (4.1.1+, 4.0.8+) reduce the impact of this issue by starting all required threads early during startup, before the system is under memory pressure.

Additional recommendations (Linux, glibc)
-----------------------------------------

When running with ``vm.overcommit_memory=2`` (recommended for PostgreSQL), we also recommend starting Patroni with the following environment variables configured:

- ``MALLOC_ARENA_MAX=1`` - reduces the amount of virtual memory allocated by glibc for multi-threaded
applications
- ``PG_MALLOC_ARENA_MAX=`` - resets the value of ``MALLOC_ARENA_MAX`` for PostgreSQL processes started by Patroni.

In addition, you may tune the following Patroni configuration parameters:

- ``thread_stack_size`` - stack size used for threads started by Patroni. Lowering this value reduces memory usage of the Patroni process. The default value set by Patroni is ``512kB``. Increase ``thread_stack_size`` if Patroni experience stack-related crashes; otherwise the default value is sufficient.
- ``thread_pool_size`` - size of the thread pool used by Patroni for asynchronous tasks and REST API communication with other members during leader race or failsafe checks. The default value is ``5``, which is sufficient for three-node clusters.
- ``restapi.thread_pool_size`` - size of the thread pool used to process REST API requests. The default value is ``5``, allowing up to five parallel REST API requests. Note that requests involving SQL queries are effectively serialized because a single database connection is used, so increasing this value typically provides no benefit.

----

PostgreSQL High Availability and Patroni
----------------------------------------

There are many ways to run high availability with PostgreSQL; for a list, see the `PostgreSQL Documentation <https://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling>`__.

Expand Down Expand Up @@ -173,5 +218,5 @@ When connecting from an application, always use a non-superuser. Patroni require

.. |Tests Status| image:: https://github.com/patroni/patroni/actions/workflows/tests.yaml/badge.svg
:target: https://github.com/patroni/patroni/actions/workflows/tests.yaml?query=branch%3Amaster
.. |Coverage Status| image:: https://coveralls.io/repos/patroni/patroni/badge.svg?branch=master
:target: https://coveralls.io/github/patroni/patroni?branch=master
.. |Coverage Status| image:: https://codecov.io/gh/patroni/patroni/graph/badge.svg?token=qWNJyFTeul
:target: https://codecov.io/gh/patroni/patroni
4 changes: 4 additions & 0 deletions docs/ENVIRONMENT.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ It is possible to override some of the configuration parameters defined in the P
Global/Universal
----------------
- **PATRONI\_CONFIGURATION**: it is possible to set the entire configuration for the Patroni via ``PATRONI_CONFIGURATION`` environment variable. In this case any other environment variables will not be considered!
- **PATRONI\_THREAD\_POOL\_SIZE**: size of thread pool used by Patroni to execute asynchronous tasks and communicate via REST API with other members during leader race or failsafe checks. Minimal value is ``5``, default value is ``5``.
- **PATRONI\_THREAD\_STACK\_SIZE**: specifies the stack size to be used for threads started by Patroni. Value must be aligned by ``64kB``. Minimal value is ``64kB``, default value (set by Patroni) is ``512kB``.
- **PATRONI\_NAME**: name of the node where the current instance of Patroni is running. Must be unique for the cluster.
- **PATRONI\_NAMESPACE**: path within the configuration store where Patroni will keep information about the cluster. Default value: "/service"
- **PATRONI\_SCOPE**: cluster name
- **PG\_MALLOC\_ARENA\_MAX**: custom value for ``MALLOC_ARENA_MAX`` environment variable for ``postmaster`` process. If not set, ``postmaster`` will inherit ``MALLOC_ARENA_MAX`` value.

Log
---
Expand Down Expand Up @@ -193,6 +196,7 @@ PostgreSQL

REST API
--------
- **PATRONI\_RESTAPI\_THREAD\_POOL\_SIZE**: size of thread pool used by Patroni to process REST API requests. Minimal value is ``5``, default value is ``5``.
- **PATRONI\_RESTAPI\_CONNECT\_ADDRESS**: IP address and port to access the REST API.
- **PATRONI\_RESTAPI\_LISTEN**: IP address and port that Patroni will listen to, to provide health-check information for HAProxy.
- **PATRONI\_RESTAPI\_USERNAME**: Basic-auth username to protect unsafe REST API endpoints.
Expand Down
Binary file added docs/_static/patroni-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/_static/patroni-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/existing_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ You can find below an overview of steps for converting an existing Postgres clus

#. Create a YAML configuration file for Patroni. You can use :ref:`Patroni configuration generation and validation tooling <validate_generate_config>` for that.

* **Note (specific for the primary node):** If you have replication slots being used for replication between cluster members, then it is recommended that you enable ``use_slots`` and configure the existing replication slots as permanent via the ``slots`` configuration item. Be aware that Patroni automatically creates replication slots for replication between members, and drops replication slots that it does not recognize, when ``use_slots`` is enabled. The idea of using permanent slots here is to allow your existing slots to persist while the migration to Patroni is in progress. See :ref:`YAML Configuration Settings <yaml_configuration>` for details.
* **Note (specific for the primary node):** If you have replication slots being used for replication between cluster members, then it is recommended that you enable ``use_slots`` and configure the existing replication slots as permanent via the ``slots`` configuration item. Be aware that Patroni automatically creates replication slots for replication between members, and drops replication slots that it does not recognize, when ``use_slots`` is enabled. The idea of using permanent slots here is to allow your existing slots to persist while the migration to Patroni is in progress. See :ref:`Dynamic Configuration Settings <dynamic_configuration>` for details.

#. Start Patroni using the ``patroni`` systemd service unit. It automatically detects that Postgres is already running and starts monitoring the instance.

Expand All @@ -47,7 +47,7 @@ You can find below an overview of steps for converting an existing Postgres clus
#. Immediate restart of the standby nodes.
#. Scheduled restart of the primary node within a maintenance window.

#. If you configured permanent slots in step ``1.2.``, then you should remove them from ``slots`` configuration through :ref:`patronictl edit-config cluster-name member-name <patronictl_edit_config_parameters>` command once the ``restart_lsn`` of the slots created by Patroni is able to catch up with the ``restart_lsn`` of the original slots for the corresponding members. By removing the slots from ``slots`` configuration you will allow Patroni to drop the original slots from your cluster once they are not needed anymore. You can find below an example query to check the ``restart_lsn`` of a couple slots, so you can compare them:
#. If you configured permanent slots in step ``1.2.``, then you should remove them from ``slots`` configuration through :ref:`patronictl edit-config cluster-name <patronictl_edit_config_parameters>` command once the ``restart_lsn`` of the slots created by Patroni is able to catch up with the ``restart_lsn`` of the original slots for the corresponding members. By removing the slots from ``slots`` configuration you will allow Patroni to drop the original slots from your cluster once they are not needed anymore. You can find below an example query to check the ``restart_lsn`` of a couple slots, so you can compare them:

.. code-block:: sql

Expand Down
10 changes: 10 additions & 0 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ How can I change my environment configuration?

Take care to not cause a failover in the cluster! You might be interested in checking :ref:`patronictl_pause`.

How can I reduce repetitive heartbeat log lines during normal operation?
If your logs are too noisy because of repeated lines like ``Lock owner: ...`` and ``no action. I am ...``,
configure ``log.deduplicate_heartbeat_logs: true``.

You can set it either in the Patroni YAML file (:ref:`log_settings`) or with
``PATRONI_LOG_DEDUPLICATE_HEARTBEAT_LOGS=true``.

Keep in mind this reduces log volume by suppressing repeated heartbeat messages, but you also lose per-loop
heartbeat visibility that can help during failover diagnostics.

What occurs if I change a Postgres GUC that requires a reload?
When you change the dynamic or the local configuration as explained in the previous questions, Patroni will take care of reloading the Postgres configuration for you.

Expand Down
2 changes: 1 addition & 1 deletion docs/ha_multi_dc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
HA multi datacenter
===================

The high availability of a PostgreSQL cluster deployed in multiple data centers is based on replication, which can be synchronous or asynchronous (`replication_modes <replication_modes.rst>`_).
The high availability of a PostgreSQL cluster deployed in multiple data centers is based on replication, which can be synchronous or asynchronous (see :ref:`replication modes <replication_modes>`).

In both cases, it is important to be clear about the following concepts:

Expand Down
Loading
Loading