patroni.postgresql.sync module

class patroni.postgresql.sync.SyncHandler(postgresql: Postgresql)View on GitHub

Bases: object

Class responsible for working with the synchronous_standby_names.

Sync standbys are chosen based on their state in pg_stat_replication. When synchronous_standby_names is changed we memorize the _primary_flush_lsn and the current_state() method will count newly added names as “sync” only when they reached memorized LSN and also reported as “sync” by pg_stat_replication

__init__(postgresql: Postgresql) NoneView on GitHub
_handle_synchronous_standby_names_change() NoneView on GitHub

Handles changes of “synchronous_standby_names” GUC.

If “synchronous_standby_names” was changed, we need to check that newly added replicas have reached self._primary_flush_lsn. Only after that they could be counted as synchronous.

_process_replica_readiness(cluster: Cluster, replica_list: _ReplicaList) NoneView on GitHub

Flags replicas as truly “synchronous” when they have caught up with _primary_flush_lsn.

Parameters:
  • cluster – current cluster topology from DCS

  • replica_list – collection of replicas that we want to evaluate.

current_state(cluster: Cluster) Tuple[CaseInsensitiveSet, CaseInsensitiveSet]View on GitHub

Find the best candidates to be the synchronous standbys.

Current synchronous standby is always preferred, unless it has disconnected or does not want to be a synchronous standby any longer.

Standbys are selected based on values from the global configuration:

  • maximum_lag_on_syncnode: would help swapping unhealthy sync replica in case if it stops responding (or hung). Please set the value high enough so it won’t unncessarily swap sync standbys during high loads. Any value less or equal of 0 keeps the behavior backward compatible. Please note that it will not also swap sync standbys in case where all replicas are hung.

  • synchronous_node_count: controlls how many nodes should be set as synchronous.

Returns:

tuple of candidates CaseInsensitiveSet and synchronous standbys CaseInsensitiveSet.

set_synchronous_standby_names(sync: Collection[str]) NoneView on GitHub

Constructs and sets “synchronous_standby_names” GUC value.

Parameters:

sync – set of nodes to sync to

class patroni.postgresql.sync._Replica(pid: int, application_name: str, sync_state: str, lsn: int, nofailover: bool)View on GitHub

Bases: NamedTuple

Class representing a single replica that is eligible to be synchronous.

Attributes are taken from pg_stat_replication view and respective Cluster.members.

Variables:
  • pid – PID of walsender process.

  • application_name – matches with the Member.name.

  • sync_state – possible values are: async, potential, quorum, and sync.

  • lsnwrite_lsn, flush_lsn, or replay_lsn, depending on the value of synchronous_commit GUC.

  • nofailover – whether the corresponding member has nofailover tag set to True.

_asdict()View on GitHub

Return a new dict which maps field names to their values.

_field_defaults = {}
_fields = ('pid', 'application_name', 'sync_state', 'lsn', 'nofailover')
classmethod _make(iterable)View on GitHub

Make a new _Replica object from a sequence or iterable

_replace(**kwds)View on GitHub

Return a new _Replica object replacing specified fields with new values

application_name: str

Alias for field number 1

lsn: int

Alias for field number 3

nofailover: bool

Alias for field number 4

pid: int

Alias for field number 0

sync_state: str

Alias for field number 2

class patroni.postgresql.sync._ReplicaList(postgresql: Postgresql, cluster: Cluster)View on GitHub

Bases: List[_Replica]

A collection of :class:_Replica objects.

Values are reverse ordered by _Replica.sync_state and _Replica.lsn. That is, first there will be replicas that have sync_state == sync, even if they are not the most up-to-date in term of write/flush/replay LSN. It helps to keep the result of chosing new synchronous nodes consistent in case if a synchronous standby member is slowed down OR async node is receiving changes faster than the sync member. Such cases would trigger sync standby member swapping, but only if lag on this member is exceeding a threshold (maximum_lag_on_syncnode).

Variables:

max_lsn – maximum value of _Replica.lsn among all values. In case if there is just one element in the list we take value of pg_current_wal_flush_lsn().

__init__(postgresql: Postgresql, cluster: Cluster) NoneView on GitHub

Create :class:_ReplicaList object.

Parameters:
  • postgresql – reference to :class:Postgresql object.

  • cluster – currently known cluster state from DCS.

class patroni.postgresql.sync._SSN(sync_type: str, has_star: bool, num: int, members: CaseInsensitiveSet)View on GitHub

Bases: NamedTuple

class representing “synchronous_standby_names” value after parsing.

Variables:
  • sync_type – possible values: ‘off’, ‘priority’, ‘quorum’

  • has_star – is set to True if “synchronous_standby_names” contains ‘*’

  • num – how many nodes are required to be synchronous

  • members – collection of standby names listed in “synchronous_standby_names”

_asdict()View on GitHub

Return a new dict which maps field names to their values.

_field_defaults = {}
_fields = ('sync_type', 'has_star', 'num', 'members')
classmethod _make(iterable)View on GitHub

Make a new _SSN object from a sequence or iterable

_replace(**kwds)View on GitHub

Return a new _SSN object replacing specified fields with new values

has_star: bool

Alias for field number 1

members: CaseInsensitiveSet

Alias for field number 3

num: int

Alias for field number 2

sync_type: str

Alias for field number 0

patroni.postgresql.sync.parse_sync_standby_names(value: str) _SSNView on GitHub

Parse postgresql synchronous_standby_names to constituent parts.

Parameters:

value – the value of synchronous_standby_names

Returns:

_SSN object

Raises:

ValueError – if the configuration value can not be parsed

>>> parse_sync_standby_names('').sync_type
'off'
>>> parse_sync_standby_names('FiRsT').sync_type
'priority'
>>> 'first' in parse_sync_standby_names('FiRsT').members
True
>>> set(parse_sync_standby_names('"1"').members)
{'1'}
>>> parse_sync_standby_names(' a , b ').members == {'a', 'b'}
True
>>> parse_sync_standby_names(' a , b ').num
1
>>> parse_sync_standby_names('ANY 4("a",*,b)').has_star
True
>>> parse_sync_standby_names('ANY 4("a",*,b)').num
4
>>> parse_sync_standby_names('1')  
Traceback (most recent call last):
    ...
ValueError: Unparseable synchronous_standby_names value
>>> parse_sync_standby_names('a,')  
Traceback (most recent call last):
    ...
ValueError: Unparseable synchronous_standby_names value
>>> parse_sync_standby_names('ANY 4("a" b,"c c")')  
Traceback (most recent call last):
    ...
ValueError: Unparseable synchronous_standby_names value
>>> parse_sync_standby_names('FIRST 4("a",)')  
Traceback (most recent call last):
    ...
ValueError: Unparseable synchronous_standby_names value
>>> parse_sync_standby_names('2 (,)')  
Traceback (most recent call last):
    ...
ValueError: Unparseable synchronous_standby_names value
patroni.postgresql.sync.quote_ident(value: str) strView on GitHub

Very simplified version of psycopg quote_ident() function.