Skip to content

Commit 5553a06

Browse files
committed
Improve patronictl multisite support
1 parent 4c6faaf commit 5553a06

File tree

5 files changed

+36
-16
lines changed

5 files changed

+36
-16
lines changed

patroni/api.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ def do_GET_metrics(self) -> None:
680680
self.write_response(200, '\n'.join(metrics)+'\n', content_type='text/plain')
681681

682682
def do_GET_multisite(self):
683-
self._write_json_response(200, {"status": self.server.patroni.multisite.status()})
683+
self._write_json_response(200, self.server.patroni.multisite.status())
684684

685685
def _read_json_content(self, body_is_optional: bool = False) -> Optional[Dict[Any, Any]]:
686686
"""Read JSON from HTTP request body.
@@ -1401,6 +1401,10 @@ def get_postgresql_status(self, retry: bool = False) -> Dict[str, Any]:
14011401
if self.server.patroni.ha.failsafe_is_active():
14021402
result['failsafe_mode_is_active'] = True
14031403
result['dcs_last_seen'] = self.server.patroni.dcs.last_seen
1404+
1405+
if self.server.patroni.multisite.is_active:
1406+
result['multisite'] = self.server.patroni.multisite.status()
1407+
14041408
return result
14051409

14061410
def handle_one_request(self) -> None:

patroni/ctl.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,16 +1502,14 @@ def get_cluster_service_info(cluster: Dict[str, Any]) -> List[str]:
15021502
"""
15031503
service_info: List[str] = []
15041504

1505-
leader_name = cluster['leader']['name'] if cluster.get('leader') else None
1506-
for m in cluster['members']:
1507-
if m['name'] == leader_name:
1508-
try:
1509-
response = request_patroni(m, endpoint="multisite")
1510-
msdata = json.loads(response.data.decode('utf-8'))
1511-
if 'status' in msdata:
1512-
service_info.append(f"Multisite {msdata['status']}")
1513-
except Exception:
1514-
pass
1505+
1506+
1507+
if 'multisite' in cluster:
1508+
info = f"Multisite {cluster['multisite']['name'] or ''} is {cluster['multisite']['status'].lower()}"
1509+
standby_config = cluster['multisite'].get('standby_config', {})
1510+
if standby_config and standby_config.get('host'):
1511+
info += f", replicating from {standby_config['host']}:{standby_config.get('port', 5432)}"
1512+
service_info.append(info)
15151513

15161514
if cluster.get('pause'):
15171515
service_info.append('Maintenance mode: on')

patroni/ha.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ def touch_member(self) -> bool:
507507
if self.is_paused():
508508
data['pause'] = True
509509

510+
if self.patroni.multisite.is_active:
511+
data['multisite'] = self.patroni.multisite.status()
512+
510513
ret = self.dcs.touch_member(data)
511514
if ret:
512515
new_state = (data['state'], data['role'])

patroni/multisite.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import time
77
import six
88

9-
from .dcs import Member
9+
from .dcs import Member, Cluster
1010
from .dcs.kubernetes import catch_kubernetes_errors, Kubernetes
1111
from .exceptions import DCSError
1212

@@ -59,7 +59,7 @@ def on_shutdown(self, checkpoint_location):
5959
class SingleSiteController(AbstractSiteController):
6060
"""Do nothing controller for single site operation."""
6161
def status(self):
62-
return "Leader"
62+
return {"status": "Leader", "active": False}
6363

6464
class MultisiteController(Thread, AbstractSiteController):
6565
is_active = True
@@ -117,7 +117,12 @@ def __init__(self, config, on_change=None):
117117
self._dcs_error = None
118118

119119
def status(self):
120-
return "Leader" if self._has_leader else "Standby"
120+
return {
121+
"status": "Leader" if self._has_leader or self._standby_config is None else "Standby",
122+
"active": True,
123+
"name": self.name,
124+
"standby_config": self.get_active_standby_config(),
125+
}
121126

122127
def get_active_standby_config(self):
123128
return self._standby_config
@@ -317,7 +322,7 @@ def _update_history(self, cluster):
317322
else:
318323
self.dcs.set_history_value(json.dumps([{'last_leader': self.name, 'switches': 0}]))
319324

320-
def _check_for_failover(self, cluster):
325+
def _check_for_failover(self, cluster: Cluster):
321326
if cluster.failover and cluster.failover.target_site:
322327
if cluster.failover.target_site == self.name:
323328
logger.info("Cleaning up failover key targeting us")

patroni/utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,9 +950,17 @@ def cluster_as_json(cluster: 'Cluster') -> Dict[str, Any]:
950950

951951
ret: Dict[str, Any] = {'members': []}
952952
sync_role = 'quorum_standby' if config.is_quorum_commit_mode else 'sync_standby'
953+
multisite_active = False
954+
multisite_info = {'status': 'leaderless'}
953955
for m in cluster.members:
956+
multisite = m.data.get('multisite', {})
957+
multisite_active = multisite_active or multisite.get('active', False)
954958
if m.name == leader_name:
955-
role = 'standby_leader' if config.is_standby_cluster else 'leader'
959+
multisite_standby = multisite.get('status') == 'Standby'
960+
multisite_info['status'] = multisite.get('status')
961+
multisite_info['name'] = multisite.get('name')
962+
multisite_info['standby_config'] = multisite.get('standby_config', {})
963+
role = 'standby_leader' if config.is_standby_cluster or multisite_standby else 'leader'
956964
elif config.is_synchronous_mode and cluster.sync.matches(m.name):
957965
role = sync_role
958966
else:
@@ -984,6 +992,8 @@ def cluster_as_json(cluster: 'Cluster') -> Dict[str, Any]:
984992
ret['members'].sort(key=cmp)
985993
if config.is_paused:
986994
ret['pause'] = True
995+
if multisite_active:
996+
ret['multisite'] = multisite_info
987997
if cluster.failover and cluster.failover.scheduled_at:
988998
ret['scheduled_switchover'] = {'at': cluster.failover.scheduled_at.isoformat()}
989999
if cluster.failover.leader:

0 commit comments

Comments
 (0)