@@ -43,8 +43,9 @@ def test_make_request_checks_thrift_status_code(self):
4343 mock_response = Mock ()
4444 mock_response .status .statusCode = ttypes .TStatusCode .ERROR_STATUS
4545 mock_method = lambda _ : mock_response
46+ thrift_backend = ThriftBackend ("foo" , 123 , "bar" , [])
4647 with self .assertRaises (DatabaseError ):
47- ThriftBackend .make_request (mock_method , Mock ())
48+ thrift_backend .make_request (mock_method , Mock ())
4849
4950 def _make_type_desc (self , type ):
5051 return ttypes .TTypeDesc (types = [ttypes .TTypeEntry (ttypes .TPrimitiveTypeEntry (type = type ))])
@@ -174,22 +175,25 @@ def test_hive_schema_to_description_preserves_column_names_and_types(self):
174175
175176 def test_make_request_checks_status_code (self ):
176177 error_codes = [ttypes .TStatusCode .ERROR_STATUS , ttypes .TStatusCode .INVALID_HANDLE_STATUS ]
178+ thrift_backend = ThriftBackend ("foo" , 123 , "bar" , [])
179+
177180 for code in error_codes :
178181 mock_error_response = Mock ()
179182 mock_error_response .status .statusCode = code
180183 mock_error_response .status .errorMessage = "a detailed error message"
181184 with self .assertRaises (DatabaseError ) as cm :
182- ThriftBackend .make_request (lambda _ : mock_error_response , Mock ())
185+ thrift_backend .make_request (lambda _ : mock_error_response , Mock ())
183186 self .assertIn ("a detailed error message" , str (cm .exception ))
184187
185188 success_codes = [
186189 ttypes .TStatusCode .SUCCESS_STATUS , ttypes .TStatusCode .SUCCESS_WITH_INFO_STATUS ,
187190 ttypes .TStatusCode .STILL_EXECUTING_STATUS
188191 ]
192+
189193 for code in success_codes :
190194 mock_response = Mock ()
191195 mock_response .status .statusCode = code
192- ThriftBackend .make_request (lambda _ : mock_response , Mock ())
196+ thrift_backend .make_request (lambda _ : mock_response , Mock ())
193197
194198 def test_handle_execute_response_checks_operation_state_in_direct_results (self ):
195199 for resp_type in self .execute_response_types :
@@ -746,6 +750,74 @@ def test_handle_execute_response_sets_active_op_handle(self):
746750
747751 self .assertEqual (mock_resp .operationHandle , mock_cursor .active_op_handle )
748752
753+ @patch ("thrift.transport.THttpClient.THttpClient" )
754+ def test_make_request_wont_retry_if_headers_not_present (self , t_transport_class ):
755+ t_transport_instance = t_transport_class .return_value
756+ t_transport_instance .code = 429
757+ t_transport_instance .headers = {"foo" : "bar" }
758+ mock_method = Mock ()
759+ mock_method .side_effect = Exception ("This method fails" )
760+
761+ thrift_backend = ThriftBackend ("foobar" , 443 , "path" , [])
762+
763+ with self .assertRaises (OperationalError ) as cm :
764+ thrift_backend .make_request (mock_method , Mock ())
765+
766+ self .assertIn ("This method fails" , str (cm .exception ))
767+
768+ @patch ("thrift.transport.THttpClient.THttpClient" )
769+ def test_make_request_wont_retry_if_error_code_not_429_or_503 (self , t_transport_class ):
770+ t_transport_instance = t_transport_class .return_value
771+ t_transport_instance .code = 430
772+ t_transport_instance .headers = {"Retry-After" : "1" }
773+ mock_method = Mock ()
774+ mock_method .side_effect = Exception ("This method fails" )
775+
776+ thrift_backend = ThriftBackend ("foobar" , 443 , "path" , [])
777+
778+ with self .assertRaises (OperationalError ) as cm :
779+ thrift_backend .make_request (mock_method , Mock ())
780+
781+ self .assertIn ("This method fails" , str (cm .exception ))
782+
783+ @patch ("thrift.transport.THttpClient.THttpClient" )
784+ def test_make_request_will_retry_max_number_of_retries_times_if_retryable (
785+ self , t_transport_class ):
786+ t_transport_instance = t_transport_class .return_value
787+ t_transport_instance .code = 429
788+ t_transport_instance .headers = {"Retry-After" : "0" }
789+ mock_method = Mock ()
790+ mock_method .side_effect = Exception ("This method fails" )
791+
792+ thrift_backend = ThriftBackend ("foobar" , 443 , "path" , [], _max_number_of_retries = 13 )
793+
794+ with self .assertRaises (OperationalError ) as cm :
795+ thrift_backend .make_request (mock_method , Mock ())
796+
797+ self .assertIn ("This method fails" , str (cm .exception ))
798+
799+ self .assertEqual (mock_method .call_count , 13 + 1 )
800+
801+ @patch ("thrift.transport.THttpClient.THttpClient" )
802+ def test_make_request_will_read_X_Thriftserver_Error_Message_if_set (self , t_transport_class ):
803+ t_transport_instance = t_transport_class .return_value
804+ t_transport_instance .code = 429
805+ t_transport_instance .headers = {
806+ "Retry-After" : "0" ,
807+ "X-Thriftserver-Error-Message" : "message2"
808+ }
809+ mock_method = Mock ()
810+ mock_method .side_effect = Exception ("This method fails" )
811+
812+ thrift_backend = ThriftBackend ("foobar" , 443 , "path" , [], _max_number_of_retries = 13 )
813+
814+ with self .assertRaises (OperationalError ) as cm :
815+ thrift_backend .make_request (mock_method , Mock ())
816+
817+ self .assertIn ("message2" , str (cm .exception ))
818+
819+ self .assertEqual (mock_method .call_count , 13 + 1 )
820+
749821
750822if __name__ == '__main__' :
751823 unittest .main ()
0 commit comments