Skip to content

Commit 8698039

Browse files
author
Jesse
authored
[ES-706907] Retry GetOperationStatus for http errors (#145)
Signed-off-by: Jesse Whitehouse <[email protected]>
1 parent 5379803 commit 8698039

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/databricks/sql/thrift_backend.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import thrift.transport.TSocket
1515
import thrift.transport.TTransport
1616

17+
import urllib3.exceptions
18+
1719
import databricks.sql.auth.thrift_http_client
1820
from databricks.sql.auth.authenticators import AuthProvider
1921
from databricks.sql.thrift_api.TCLIService import TCLIService, ttypes
@@ -324,6 +326,18 @@ def attempt_request(attempt):
324326

325327
logger.debug("Received response: {}".format(response))
326328
return response
329+
330+
except urllib3.exceptions.HTTPError as err:
331+
# retry on timeout. Happens a lot in Azure and it is safe as data has not been sent to server yet
332+
333+
gos_name = TCLIServiceClient.GetOperationStatus.__name__
334+
if method.__name__ == gos_name:
335+
retry_delay = bound_retry_delay(attempt, self._retry_delay_default)
336+
logger.info(
337+
f"GetOperationStatus failed with HTTP error and will be retried: {str(err)}"
338+
)
339+
else:
340+
raise err
327341
except OSError as err:
328342
error = err
329343
error_message = str(err)

tests/unit/test_thrift_backend.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ssl import CERT_NONE, CERT_REQUIRED
77

88
import pyarrow
9+
import urllib3
910

1011
import databricks.sql
1112
from databricks.sql.thrift_api.TCLIService import ttypes
@@ -1033,7 +1034,7 @@ def test_handle_execute_response_sets_active_op_handle(self):
10331034

10341035
self.assertEqual(mock_resp.operationHandle, mock_cursor.active_op_handle)
10351036

1036-
@patch("thrift.transport.THttpClient.THttpClient")
1037+
@patch("databricks.sql.auth.thrift_http_client.THttpClient")
10371038
@patch("databricks.sql.thrift_api.TCLIService.TCLIService.Client.GetOperationStatus")
10381039
@patch("databricks.sql.thrift_backend._retry_policy", new_callable=retry_policy_factory)
10391040
def test_make_request_will_retry_GetOperationStatus(
@@ -1089,6 +1090,51 @@ def test_make_request_will_retry_GetOperationStatus(
10891090
# The warnings should include this text
10901091
self.assertIn(f"{this_gos_name} failed with code {errno.EEXIST} and will attempt to retry", cm.output[0])
10911092

1093+
@patch("databricks.sql.thrift_api.TCLIService.TCLIService.Client.GetOperationStatus")
1094+
@patch("databricks.sql.thrift_backend._retry_policy", new_callable=retry_policy_factory)
1095+
def test_make_request_will_retry_GetOperationStatus_for_http_error(
1096+
self, mock_retry_policy, mock_gos):
1097+
1098+
import urllib3.exceptions
1099+
mock_gos.side_effect = urllib3.exceptions.HTTPError("Read timed out")
1100+
1101+
import thrift, errno
1102+
from databricks.sql.thrift_api.TCLIService.TCLIService import Client
1103+
from databricks.sql.exc import RequestError
1104+
from databricks.sql.utils import NoRetryReason
1105+
from databricks.sql.auth.thrift_http_client import THttpClient
1106+
1107+
this_gos_name = "GetOperationStatus"
1108+
mock_gos.__name__ = this_gos_name
1109+
1110+
protocol = thrift.protocol.TBinaryProtocol.TBinaryProtocol(THttpClient)
1111+
client = Client(protocol)
1112+
1113+
req = ttypes.TGetOperationStatusReq(
1114+
operationHandle=self.operation_handle,
1115+
getProgressUpdate=False,
1116+
)
1117+
1118+
EXPECTED_RETRIES = 2
1119+
1120+
thrift_backend = ThriftBackend(
1121+
"foobar",
1122+
443,
1123+
"path", [],
1124+
auth_provider=AuthProvider(),
1125+
_retry_stop_after_attempts_count=EXPECTED_RETRIES,
1126+
_retry_delay_default=1)
1127+
1128+
1129+
with self.assertRaises(RequestError) as cm:
1130+
thrift_backend.make_request(client.GetOperationStatus, req)
1131+
1132+
1133+
self.assertEqual(NoRetryReason.OUT_OF_ATTEMPTS.value, cm.exception.context["no-retry-reason"])
1134+
self.assertEqual(f'{EXPECTED_RETRIES}/{EXPECTED_RETRIES}', cm.exception.context["attempt"])
1135+
1136+
1137+
10921138

10931139
@patch("thrift.transport.THttpClient.THttpClient")
10941140
def test_make_request_wont_retry_if_headers_not_present(self, t_transport_class):

0 commit comments

Comments
 (0)