diff --git a/docs/rest_api.rst b/docs/rest_api.rst index 79b12461..ad1ac75c 100644 --- a/docs/rest_api.rst +++ b/docs/rest_api.rst @@ -44,8 +44,11 @@ For all health check ``GET`` requests Patroni returns a JSON document with the s - ``GET /synchronous`` or ``GET /sync``: returns HTTP status code **200** only when the Patroni node is running as a synchronous standby. +- ``GET /read-only-sync``: like the above endpoint, but also includes the primary. + - ``GET /asynchronous`` or ``GET /async``: returns HTTP status code **200** only when the Patroni node is running as an asynchronous standby. + - ``GET /asynchronous?lag=`` or ``GET /async?lag=``: asynchronous standby check endpoint. In addition to checks from ``asynchronous`` or ``async``, it also checks replication latency and returns status code **200** only when it is below specified value. The key cluster.last_leader_operation from DCS is used for Leader wal position and compute latency on replica for performance reasons. max-lag can be specified in bytes (integer) or in human readable values, for e.g. 16kB, 64MB, 1GB. - ``GET /async?lag=1048576`` diff --git a/patroni/api.py b/patroni/api.py index 40b3483d..d2fd36b2 100644 --- a/patroni/api.py +++ b/patroni/api.py @@ -152,6 +152,11 @@ class RestApiHandler(BaseHTTPRequestHandler): status_code = replica_status_code elif path in ('/async', '/asynchronous') and not is_synchronous: status_code = replica_status_code + elif path in ('/read-only-sync', '/read-only-synchronous'): + if 200 in (primary_status_code, standby_leader_status_code): + status_code = 200 + elif is_synchronous: + status_code = replica_status_code # check for user defined tags in query params if not ignore_tags and status_code == 200: diff --git a/tests/test_api.py b/tests/test_api.py index e83edfef..107bb638 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -169,6 +169,7 @@ class TestRestApiHandler(unittest.TestCase): MockRestApiServer(RestApiHandler, 'GET /replica?lag=10MB') MockRestApiServer(RestApiHandler, 'GET /replica?lag=10485760') MockRestApiServer(RestApiHandler, 'GET /read-only') + MockRestApiServer(RestApiHandler, 'GET /read-only-sync') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={})): MockRestApiServer(RestApiHandler, 'GET /replica') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'master'})): @@ -180,6 +181,8 @@ class TestRestApiHandler(unittest.TestCase): 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 /read-only-sync') with patch.object(RestApiHandler, 'get_postgresql_status', Mock(return_value={'role': 'replica'})): MockPatroni.dcs.cluster.sync.members = [] MockRestApiServer(RestApiHandler, 'GET /asynchronous')