diff --git a/src/databricks/sql/auth/common.py b/src/databricks/sql/auth/common.py index cda30a528..a0976e78c 100644 --- a/src/databricks/sql/auth/common.py +++ b/src/databricks/sql/auth/common.py @@ -73,11 +73,16 @@ def __init__( # HTTP client configuration self.ssl_options = ssl_options self.socket_timeout = socket_timeout - self.retry_stop_after_attempts_count = retry_stop_after_attempts_count or 5 + # Defaults align with the connector-wide retry defaults used by the + # Thrift and SEA backends (30 attempts / 900.0s), which are anchored to + # the ODBC/JDBC drivers. UnifiedHttpClient (used by CloudFetch downloads) + # builds its retry policy from this context, so diverging here cut + # cloudfetch retries off far too early (see issue #709). + self.retry_stop_after_attempts_count = retry_stop_after_attempts_count or 30 self.retry_delay_min = retry_delay_min or 1.0 self.retry_delay_max = retry_delay_max or 10.0 self.retry_stop_after_attempts_duration = ( - retry_stop_after_attempts_duration or 300.0 + retry_stop_after_attempts_duration or 900.0 ) self.retry_delay_default = retry_delay_default or 5.0 self.retry_dangerous_codes = retry_dangerous_codes or [] diff --git a/tests/unit/test_client_context_retry_defaults.py b/tests/unit/test_client_context_retry_defaults.py new file mode 100644 index 000000000..ccc5a7da8 --- /dev/null +++ b/tests/unit/test_client_context_retry_defaults.py @@ -0,0 +1,38 @@ +""" +Unit tests for ClientContext / build_client_context retry-policy defaults. + +Regression tests for issue #709: CloudFetch downloads go through +UnifiedHttpClient, which builds its DatabricksRetryPolicy from ClientContext. +When the user does not override the retry settings, those defaults must match +the connector-wide defaults used by the Thrift and SEA backends (30 attempts / +900.0s), which are anchored to the ODBC/JDBC drivers (see +thrift_backend.py: "900s attempts-duration lines up w ODBC/JDBC drivers"). +Previously ClientContext fell back to 5 attempts / 300.0s, cutting cloudfetch +retries off far earlier than the rest of the connector. +""" + +from databricks.sql.auth.common import ClientContext +from databricks.sql.utils import build_client_context + + +class TestClientContextRetryDefaults: + def test_default_max_retry_duration_is_900(self): + """With no override, retry duration defaults to 900.0s (not 300.0).""" + context = build_client_context("test.databricks.com", "test-version") + assert context.retry_stop_after_attempts_duration == 900.0 + + def test_default_max_retry_count_is_30(self): + """With no override, retry attempt count defaults to 30 (not 5).""" + context = build_client_context("test.databricks.com", "test-version") + assert context.retry_stop_after_attempts_count == 30 + + def test_user_override_still_honored(self): + """Explicit user overrides are propagated unchanged.""" + context = build_client_context( + "test.databricks.com", + "test-version", + _retry_stop_after_attempts_duration=120.0, + _retry_stop_after_attempts_count=7, + ) + assert context.retry_stop_after_attempts_duration == 120.0 + assert context.retry_stop_after_attempts_count == 7