Repository: dbt-labs/dbt-redshift (python)

Issue:
[ADAP-1022] [Feature] improve handling of disconnect/broken pipe exceptions thrown by redshift-connector
### Is this your first time submitting a feature request?

- [X] I have read the [expectations for open source contributors](https://docs.getdbt.com/docs/contributing/oss-expectations)
- [X] I have searched the existing issues, and I could not find an existing issue for this feature
- [X] I am requesting a straightforward extension of existing dbt-redshift functionality, rather than a Big Idea better suited to a discussion

### Describe the feature

Based on commentary from the redshift-connector team (i.e.  [this](https://github.com/aws/amazon-redshift-python-driver/issues/142) and now that it surfaces better error messages we should update our connection logic to take advantage. 

Specifically we should retry based on the number of configured retries all `InterfaceError`  and `BrokenPipe` exceptions

### Describe alternatives you've considered

_No response_

### Who will this benefit?

_No response_

### Are you interested in contributing this feature?

_No response_

### Anything else?

_No response_

Pull request:
add InterfaceError to retryable_exceptions
- add InterfaceError to retryable_exceptions
- add changie

resolves #661 
[docs](https://github.com/dbt-labs/docs.getdbt.com/issues/new/choose) dbt-labs/docs.getdbt.com/#

<!---
  Include the number of the issue addressed by this PR above if applicable.
  PRs for code changes without an associated issue *will not be merged*.
  See CONTRIBUTING.md for more information.

  Include the number of the docs issue that was opened for this PR. If
  this change has no user-facing implications, "N/A" suffices instead. New
  docs tickets can be created by clicking the link above or by going to
  https://github.com/dbt-labs/docs.getdbt.com/issues/new/choose.
-->

### Problem

<!---
  Describe the problem this PR is solving. What is the application state
  before this PR is merged?
-->

### Solution

<!---
  Describe the way this PR solves the above problem. Add as much detail as you
  can to help reviewers understand your changes. Include any alternatives and
  tradeoffs you considered.
-->

### Checklist

- [ ] I have read [the contributing guide](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md) and understand what's expected of me
- [ ] I have run this code in development and it appears to resolve the stated issue
- [ ] This PR includes tests, or tests are not required/relevant for this PR
- [ ] This PR has no interface changes (e.g. macros, cli, logs, json artifacts, config files, adapter interface, etc) or this PR has already received feedback and approval from Product or DX


Patch:
diff --git a/.changes/unreleased/Fixes-20240806-163017.yaml b/.changes/unreleased/Fixes-20240806-163017.yaml
new file mode 100644
index 00000000..20b59593
--- /dev/null
+++ b/.changes/unreleased/Fixes-20240806-163017.yaml
@@ -0,0 +1,6 @@
+kind: Fixes
+body: add InterfaceError to retryable_exceptions
+time: 2024-08-06T16:30:17.348677-07:00
+custom:
+  Author: colin-rogers-dbt
+  Issue: "661"
diff --git a/dbt/adapters/redshift/connections.py b/dbt/adapters/redshift/connections.py
index d3fbcafe..521bcff8 100644
--- a/dbt/adapters/redshift/connections.py
+++ b/dbt/adapters/redshift/connections.py
@@ -382,6 +382,7 @@ class RedshiftConnectionManager(SQLConnectionManager):
             redshift_connector.OperationalError,
             redshift_connector.DatabaseError,
             redshift_connector.DataError,
+            redshift_connector.InterfaceError,
         ]
 
         open_connection = cls.retry_connection(


Test patch:
diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py
index 17a67628..61df7a47 100644
--- a/tests/unit/test_connection.py
+++ b/tests/unit/test_connection.py
@@ -1,5 +1,8 @@
 from multiprocessing import get_context
 from unittest import TestCase, mock
+
+import pytest
+from dbt.adapters.exceptions import FailedToConnectError
 from unittest.mock import MagicMock, call
 
 import redshift_connector
@@ -7,6 +10,7 @@ import redshift_connector
 from dbt.adapters.redshift import (
     Plugin as RedshiftPlugin,
     RedshiftAdapter,
+    RedshiftCredentials,
 )
 from tests.unit.utils import (
     config_from_parts_or_dicts,
@@ -128,3 +132,32 @@ class TestConnection(TestCase):
                     call(f"select pg_terminate_backend({backend_pid})"),
                 ]
             )
+
+    def test_retry_able_exceptions_trigger_retry(self):
+        with mock.patch.object(self.adapter.connections, "add_query") as add_query:
+            connection_mock = mock_connection("model", state="closed")
+            connection_mock.credentials = RedshiftCredentials.from_dict(
+                {
+                    "type": "redshift",
+                    "dbname": "redshift",
+                    "user": "root",
+                    "host": "thishostshouldnotexist.test.us-east-1",
+                    "pass": "password",
+                    "port": 5439,
+                    "schema": "public",
+                    "retries": 2,
+                }
+            )
+
+            connect_mock = MagicMock()
+            connect_mock.side_effect = [
+                redshift_connector.InterfaceError("retryable interface error<1>"),
+                redshift_connector.InterfaceError("retryable interface error<2>"),
+                redshift_connector.InterfaceError("retryable interface error<3>"),
+            ]
+
+            with mock.patch("redshift_connector.connect", connect_mock):
+                with pytest.raises(FailedToConnectError) as e:
+                    connection = self.adapter.connections.open(connection_mock)
+            assert str(e.value) == "Database Error\n  retryable interface error<3>"
+            assert connect_mock.call_count == 3