Skip to content

Commit

Permalink
Merge pull request #436 from patroni/master
Browse files Browse the repository at this point in the history
Syncing from upstream patroni/patroni (master)
  • Loading branch information
bt-admin authored Aug 16, 2024
2 parents a22b69e + 7659ccd commit 4528c49
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 9 deletions.
13 changes: 13 additions & 0 deletions patroni/dcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,19 @@ def conn_kwargs(self, auth: Union[Any, Dict[str, Any], None] = None) -> Dict[str
ret['user'] = ret.pop('username')
return ret

def get_endpoint_url(self, endpoint: Optional[str] = None) -> str:
"""Get URL from member :attr:`~Member.api_url` and endpoint.
:param endpoint: URL path of REST API.
:returns: full URL for this REST API.
"""
url = self.api_url or ''
if endpoint:
scheme, netloc, _, _, _, _ = urlparse(url)
url = urlunparse((scheme, netloc, endpoint, '', '', ''))
return url

@property
def api_url(self) -> Optional[str]:
"""The ``api_url`` value from :attr:`~Member.data` if defined."""
Expand Down
8 changes: 5 additions & 3 deletions patroni/ha.py
Original file line number Diff line number Diff line change
Expand Up @@ -1174,15 +1174,17 @@ def call_failsafe_member(self, data: Dict[str, Any], member: Member) -> _Failsaf
:returns: a :class:`_FailsafeResponse` object.
"""
endpoint = 'failsafe'
url = member.get_endpoint_url(endpoint)
try:
response = self.patroni.request(member, 'post', 'failsafe', data, timeout=2, retries=1)
response = self.patroni.request(member, 'post', endpoint, data, timeout=2, retries=1)
response_data = response.data.decode('utf-8')
logger.info('Got response from %s %s: %s', member.name, member.api_url, response_data)
logger.info('Got response from %s %s: %s', member.name, url, response_data)
accepted = response.status == 200 and response_data == 'Accepted'
# member may return its current received/replayed LSN in the "lsn" header.
return _FailsafeResponse(member.name, accepted, parse_int(response.headers.get('lsn')))
except Exception as e:
logger.warning("Request failed to %s: POST %s (%s)", member.name, member.api_url, e)
logger.warning("Request failed to %s: POST %s (%s)", member.name, url, e)
return _FailsafeResponse(member.name, False, None)

def check_failsafe_topology(self) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion patroni/postgresql/postmaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def _is_postmaster_process(self) -> bool:
start_time = int(self._postmaster_pid.get('start_time', 0))
if start_time and abs(self.create_time() - start_time) > 3:
logger.info('Process %s is not postmaster, too much difference between PID file start time %s and '
'process start time %s', self.pid, self.create_time(), start_time)
'process start time %s', self.pid, start_time, self.create_time())
return False
except ValueError:
logger.warning('Garbage start time value in pid file: %r', self._postmaster_pid.get('start_time'))
Expand Down
6 changes: 1 addition & 5 deletions patroni/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import json

from typing import Any, Dict, Optional, Union
from urllib.parse import urlparse, urlunparse

import urllib3

Expand Down Expand Up @@ -164,10 +163,7 @@ def __call__(self, member: Member, method: str = 'GET', endpoint: Optional[str]
:returns: the response returned upon request.
"""
url = member.api_url or ''
if endpoint:
scheme, netloc, _, _, _, _ = urlparse(url)
url = urlunparse((scheme, netloc, endpoint, '', '', ''))
url = member.get_endpoint_url(endpoint)
return self.request(method, url, data, **kwargs)


Expand Down
17 changes: 17 additions & 0 deletions tests/test_ha.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,23 @@ def test_update_failsafe(self):
self.p.set_role('primary')
self.assertEqual(self.ha.update_failsafe({}), 'Running as a leader')

def test_call_failsafe_member(self):
member = Member(0, 'test', 1, {'api_url': 'http://localhost:8011/patroni'})
self.ha.patroni.request = Mock()
self.ha.patroni.request.return_value.data = b'Accepted'
self.ha.patroni.request.return_value.status = 200
with patch('patroni.ha.logger.info') as mock_logger:
ret = self.ha.call_failsafe_member({}, member)
self.assertEqual(mock_logger.call_args_list[0][0], ('Got response from %s %s: %s', 'test', 'http://localhost:8011/failsafe', 'Accepted'))
self.assertTrue(ret.accepted)

e = Exception('request failed')
self.ha.patroni.request.side_effect = e
with patch('patroni.ha.logger.warning') as mock_logger:
ret = self.ha.call_failsafe_member({}, member)
self.assertEqual(mock_logger.call_args_list[0][0], ('Request failed to %s: POST %s (%s)', 'test', 'http://localhost:8011/failsafe', e))
self.assertFalse(ret.accepted)

@patch('time.sleep', Mock())
def test_bootstrap_from_another_member(self):
self.ha.cluster = get_cluster_initialized_with_leader()
Expand Down

0 comments on commit 4528c49

Please sign in to comment.