Skip to content

Commit f486efe

Browse files
authored
Merge pull request #125 from hnousiainen/htn_pglookout_failover_priorities
pglookout: support explicit failover priorities
2 parents 60f65b2 + 56fc154 commit f486efe

File tree

5 files changed

+280
-157
lines changed

5 files changed

+280
-157
lines changed

README.rst

+8
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ over_warning_limit_command and to create a warning file.
295295

296296
Shell command to execute in case the node has deemed itself in need of promotion
297297

298+
``failover_priorities`` (default ``{}``)
299+
300+
Define priority of nodes for promotion, in case there are multiple candidates
301+
with the same replication position. This allows to ensure all pglookout instances
302+
would elect the same standby for promotion, while still allowing for topologies
303+
with e.g. less preferred standbys in secondary network locations. By default,
304+
pglookout uses remote connection ids for the same selection purpose.
305+
298306
``known_gone_nodes`` (default ``[]``)
299307

300308
Lists nodes that are explicitly known to have left the cluster. If the old

pglookout/pglookout.py

+25-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
This file is under the Apache License, Version 2.0.
88
See the file `LICENSE` for details.
99
"""
10+
1011
from . import logutil, statsd, version
1112
from .cluster_monitor import ClusterMonitor
1213
from .common import convert_xlog_location_to_offset, get_iso_timestamp, parse_iso_datetime
@@ -643,19 +644,35 @@ def do_failover_decision(self, standby_nodes):
643644
if not known_replication_positions:
644645
self.log.warning("No known replication positions, canceling failover consideration")
645646
return
646-
# If there are multiple nodes with the same replication positions pick the one with the "highest" name
647-
# to make sure pglookouts running on all standbys make the same decision. The rationale for picking
648-
# the "highest" node is that there's no obvious way for pglookout to decide which of the nodes is
649-
# "best" beyond looking at replication positions, but picking the highest id supports environments
650-
# where nodes are assigned identifiers from an incrementing sequence identifiers and where we want to
651-
# promote the latest and greatest node. In static environments node identifiers can be priority
652-
# numbers, with the highest number being the one that should be preferred.
653-
furthest_along_instance = max(known_replication_positions[max(known_replication_positions)])
647+
648+
# Find the instance that is furthest along.
649+
# If there are multiple nodes with the same replication positions, try to identify one to promote either
650+
# via explicit failover priority configuration or pick the one with the "highest" name by sort order.
651+
# The rationale of this logic is to ensure all participating pglookouts running on all standbys make
652+
# the same decision. The "highest" name works well in environments where nodes are assigned identifiers
653+
# from an incrementing sequence and where we want to promote the latest and greatest node.
654+
655+
# First, find the list of instances that share the more recent replication position
656+
furthest_along_instances = known_replication_positions[max(known_replication_positions)]
657+
# Second, sort them by "instance name"
658+
furthest_along_instances = sorted(furthest_along_instances, reverse=True)
659+
# Third, if we have explicit failover priorities, use those for selecting the to be promoted instance
660+
if "failover_priorities" in self.config:
661+
highest_priority = max(
662+
self.config["failover_priorities"].get(instance, 0) for instance in furthest_along_instances
663+
)
664+
furthest_along_instances = [
665+
instance
666+
for instance in furthest_along_instances
667+
if self.config["failover_priorities"].get(instance) == highest_priority
668+
]
669+
furthest_along_instance = furthest_along_instances[0]
654670
self.log.warning(
655671
"Node that is furthest along is: %r, all replication positions were: %r",
656672
furthest_along_instance,
657673
sorted(known_replication_positions),
658674
)
675+
659676
total_observers = len(self.connected_observer_nodes) + len(self.disconnected_observer_nodes)
660677
# +1 in the calculation comes from the master node
661678
total_amount_of_nodes = len(standby_nodes) + 1 - len(self.never_promote_these_nodes) + total_observers

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ exclude = [
4747
'test/test_lookout.py',
4848
'test/test_pgutil.py',
4949
'test/test_webserver.py',
50+
'test/utils.py',
5051
# Other.
5152
'setup.py',
5253
'version.py',

0 commit comments

Comments
 (0)