diff --git a/features/basic_replication.feature b/features/basic_replication.feature index 67b44004..24d0d28f 100644 --- a/features/basic_replication.feature +++ b/features/basic_replication.feature @@ -19,6 +19,11 @@ Feature: basic replication And I run patronictl.py restart batman postgres1 --force Then I receive a response returncode 0 And "sync" key in DCS has sync_standby=postgres2 after 10 seconds + And I sleep for 2 seconds + When I issue a GET request to http://127.0.0.1:8010/sync + Then I receive a response code 200 + When I issue a GET request to http://127.0.0.1:8009/async + Then I receive a response code 200 Scenario: check the basic failover in synchronous mode Given I run patronictl.py pause batman diff --git a/patroni/api.py b/patroni/api.py index 4e457765..865de459 100644 --- a/patroni/api.py +++ b/patroni/api.py @@ -82,6 +82,14 @@ class RestApiHandler(BaseHTTPRequestHandler): patroni = self.server.patroni cluster = patroni.dcs.cluster + + def is_synchronous(): + return (cluster.is_synchronous_mode() and cluster.sync + and cluster.sync.sync_standby == patroni.postgresql.name) + + def is_balanceable_replica(): + return response.get('role') == 'replica' and not patroni.noloadbalance + if cluster: # dcs available if cluster.leader and cluster.leader.name == patroni.postgresql.name: # is_leader status_code = 200 if 'master' in path else 503 @@ -89,6 +97,10 @@ class RestApiHandler(BaseHTTPRequestHandler): status_code = 503 elif response['role'] == 'master': # running as master but without leader lock!!!! status_code = 503 + elif path in ('/sync', '/synchronous'): + status_code = 200 if is_balanceable_replica() and is_synchronous() else 503 + elif path in ('/async', '/asynchronous'): + status_code = 200 if is_balanceable_replica() and not is_synchronous() else 503 elif response['role'] in path: # response['role'] != 'master' status_code = 503 if patroni.noloadbalance else 200 else: diff --git a/tests/test_api.py b/tests/test_api.py index 0945711c..1da83d7d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -100,7 +100,7 @@ class MockPatroni(object): dcs = Mock() tags = {} version = '0.00' - noloadbalance = Mock(return_value=False) + noloadbalance = PropertyMock(return_value=False) scheduled_restart = {'schedule': future_restart_time, 'postmaster_start_time': postgresql.postmaster_start_time()} @@ -148,6 +148,12 @@ class TestRestApiHandler(unittest.TestCase): with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'master'})): MockRestApiServer(RestApiHandler, 'GET /replica') MockRestApiServer(RestApiHandler, 'GET /master') + MockPatroni.dcs.cluster.sync.sync_standby = MockPostgresql.name + MockPatroni.dcs.cluster.is_synchronous_mode = Mock(return_value=True) + with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'replica'})): + MockRestApiServer(RestApiHandler, 'GET /synchronous') + with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'replica'})): + MockRestApiServer(RestApiHandler, 'GET /asynchronous') MockPatroni.dcs.cluster.leader.name = MockPostgresql.name MockRestApiServer(RestApiHandler, 'GET /replica') MockPatroni.dcs.cluster = None