Merge branch 'feature/quorum-commit' of github.com:zalando/patroni into feature/quorum-commit

This commit is contained in:
Alexander Kukushkin
2023-07-26 07:37:00 +02:00
3 changed files with 12 additions and 12 deletions
+2 -2
View File
@@ -66,7 +66,7 @@ Note: Because of the way synchronous replication is implemented in PostgreSQL it
Synchronous Replication Factor
==============================
The parameter ``synchronous_node_count`` is used by Patroni to manage number of synchronous standby databases. It is set to 1 by default. It has no effect when ``synchronous_mode`` is set to off. When enabled, Patroni manages precise number of synchronous standby databases based on parameter ``synchronous_node_count`` and adjusts the state in DCS & ``synchronous_standby_names`` in PostgreSQL as members join and leave. If the parameter is set to a value higher than the number of eligible nodes it will be automatically reduced by Patroni down.
The parameter ``synchronous_node_count`` is used by Patroni to manage the number of synchronous standby databases. It is set to ``1`` by default. It has no effect when ``synchronous_mode`` is set to ``off``. When enabled, Patroni manages the precise number of synchronous standby databases based on parameter ``synchronous_node_count`` and adjusts the state in DCS & ``synchronous_standby_names`` in PostgreSQL as members join and leave. If the parameter is set to a value higher than the number of eligible nodes it will be automatically reduced by Patroni.
Maximum lag on synchronous node
@@ -173,7 +173,7 @@ postgresql.conf
synchronous_standby_names = 'ANY 2 (node1,node2,node3)'
If the primary (``node0``) failed, in the above example two of the ``node1``, ``node2``, ``node3`` will have the latest transaction received, but we don't know which ones. To figure out whether the node ``node1`` has received the latest transaction, we needs to compare its LSN with the LSN on **at least** one node (``quorum=1`` in the ``/sync`` key) of ``node2`` and ``node3``. If ``node1`` isn't behind of at least one of them, we can guaranty that there will be no user visible data loss if ``node1`` is promoted.
If the primary (``node0``) failed, in the above example two of the ``node1``, ``node2``, ``node3`` will have the latest transaction received, but we don't know which ones. To figure out whether the node ``node1`` has received the latest transaction, we need to compare its LSN with the LSN on **at least** one node (``quorum=1`` in the ``/sync`` key) among ``node2`` and ``node3``. If ``node1`` isn't behind of at least one of them, we can guarantee that there will be no user visible data loss if ``node1`` is promoted.
.. [1] The data is still there, but recovering it requires a manual recovery effort by data recovery specialists. When Patroni is allowed to rewind with ``use_pg_rewind`` the forked timeline will be automatically erased to rejoin the failed primary with the cluster.
+9 -9
View File
@@ -158,7 +158,7 @@ class _SyncState(NamedTuple):
:ivar sync_type: possible values: 'off', 'priority', 'quorum'
:ivar numsync: how many nodes are required to be synchronous (according to ``synchronous_standby_names``).
Is ``0`` in case if synchronous_standby_names value is invalid or has ``*``.
Is ``0`` in case if ``synchronous_standby_names`` value is invalid or has ``*``.
:ivar numsync_confirmed: how many nodes are known to be synchronous according to the ``pg_stat_replication``
view. Only nodes that caught up with the ``SyncHandler._primary_flush_lsn` are counted.
:ivar sync: collection of synchronous node names. In case of quorum commit all nodes listed
@@ -222,21 +222,21 @@ END;$$""")
self._postgresql.reset_cluster_info_state(None) # Reset internal cache to query fresh values
def _get_replica_list(self, cluster: Cluster) -> Iterator[Tuple[int, str, str, int, bool]]:
"""Yields candidates based on higher replay/remote_write/flush lsn.
"""Yields candidates based on higher replay/write/flush LSN.
.. note::
Tuples are reverse ordered by sync_state and LSN fields so nodes that already synchronous or having
Tuples are reverse ordered by ``sync_state`` and LSN fields so nodes that already synchronous or having
higher LSN values are preferred.
:param cluster: current cluster topology from DCS.
:yields: tuples composed of:
* pid - a PID of walsender process
* member name - matches with the application_name
* sync_state - one of ("async", "potential", "quorum", "sync")
* LSN - write_lsn, flush_lsn, or replica_lsn, depending on the value of ``synchronous_commit`` GUC
* nofailover - whether the member has ``nofailover`` tag set
* ``pid`` - PID of the walsender process
* ``member name`` - matches with the ``application_name```
* ``sync_state`` - one of (``async``, ``potential``, ``quorum``, ``sync``)
* ``LSN`` - write_lsn, flush_lsn, or replica_lsn, depending on the value of ``synchronous_commit`` GUC
* ``nofailover`` - whether the member has ``nofailover`` tag set
"""
# What column from pg_stat_replication we want to sort on? Choose based on ``synchronous_commit`` value.
@@ -262,7 +262,7 @@ END;$$""")
yield (pid, member.name, sync_state, replica_lsn, bool(member.nofailover))
def _process_replica_readiness(self, cluster: Cluster, replica_list: List[Tuple[int, str, str, int, bool]]) -> None:
"""Flags replicas as truely "synchronous" when they caught up with "_primary_flush_lsn"."""
"""Flags replicas as truly "synchronous" when they caught up with "_primary_flush_lsn"."""
if TYPE_CHECKING: # pragma: no cover
assert self._postgresql.global_config is not None
for pid, app_name, sync_state, replica_lsn, _ in replica_list:
+1 -1
View File
@@ -769,7 +769,7 @@ def cluster_as_json(cluster: 'Cluster', global_config: Optional['GlobalConfig']
for m in cluster.members:
if m.name == leader_name:
role = 'standby_leader' if global_config.is_standby_cluster else 'leader'
elif cluster.sync.matches(m.name, global_config.is_quorum_commit_mode):
elif cluster.sync.matches(m.name):
role = sync_role
else:
role = 'replica'