diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index f011a54d..07b5b68d 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -1094,6 +1094,32 @@ def test_429_error_retry(monkeypatch): assert post_retry.retry_count == 3 +def test_empty_200_response_retry(monkeypatch): + http_resp = TrinoRequest.http.Response() + http_resp.status_code = 200 + http_resp._content = b"" + + post_retry = RetryRecorder(result=http_resp) + monkeypatch.setattr(TrinoRequest.http.Session, "post", post_retry) + + get_retry = RetryRecorder(result=http_resp) + monkeypatch.setattr(TrinoRequest.http.Session, "get", get_retry) + + attempts = 3 + req = TrinoRequest( + host="coordinator", + port=8080, + client_session=ClientSession(user="test"), + max_attempts=attempts, + ) + + req.post("SELECT 1") + assert post_retry.retry_count == attempts + + req.get("URL") + assert get_retry.retry_count == attempts + + @pytest.mark.parametrize("status_code", [ 501 ]) diff --git a/trino/client.py b/trino/client.py index 97ef11ae..adf53766 100644 --- a/trino/client.py +++ b/trino/client.py @@ -641,6 +641,9 @@ def max_attempts(self, value: int) -> None: # need retry when there is no exception but the status code is 429, 502, 503, or 504 lambda response: getattr(response, "status_code", None) in (429, 502, 503, 504), + # need retry when the server returns 200 with an empty body (transient under load) + lambda response: getattr(response, "status_code", None) == 200 + and not getattr(response, "text", "").strip(), ), max_attempts=self._max_attempts, ) @@ -723,6 +726,10 @@ def process(self, http_response: Response) -> TrinoStatus: self.raise_response_error(http_response) http_response.encoding = "utf-8" + if not http_response.text.strip(): + raise exceptions.TrinoConnectionError( + "received empty response from server (status 200)" + ) response = json.loads(http_response.text) if "error" in response and response["error"]: raise self._process_error(response["error"], response.get("id"))