Skip to content

Commit

Permalink
Fix request URL in failsafe handling logs (patroni#3126)
Browse files Browse the repository at this point in the history
  • Loading branch information
waynerv authored Aug 15, 2024
1 parent a03dba0 commit 7659ccd
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 8 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
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 7659ccd

Please sign in to comment.