Skip to content

Commit ba06915

Browse files
committed
feat: add HTTP retries and resilient execute_sql
Configure urllib3 retries on the SDK client and retry transient connection errors when running SQL. Also drop the invalid offset argument from list_run_history.
1 parent 3e5d531 commit ba06915

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

hotdata_runtime/client.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import time
55
from typing import Any, Iterator
66

7+
from urllib3.exceptions import HTTPError as Urllib3HTTPError
8+
from urllib3.exceptions import ProtocolError
9+
710
from hotdata import ApiClient, Configuration
811
from hotdata.api.connections_api import ConnectionsApi
912
from hotdata.api.information_schema_api import InformationSchemaApi
@@ -23,6 +26,7 @@
2326
normalize_host,
2427
pick_workspace,
2528
)
29+
from hotdata_runtime.http import default_http_retries
2630
from hotdata_runtime.result import QueryResult
2731

2832
_TERMINAL = frozenset({"succeeded", "failed", "cancelled"})
@@ -71,6 +75,7 @@ def __init__(
7175
api_key=api_key,
7276
workspace_id=workspace_id,
7377
session_id=session_id,
78+
retries=default_http_retries(),
7479
)
7580
self._api = ApiClient(self._config)
7681

@@ -150,9 +155,8 @@ def list_run_history(
150155
self,
151156
*,
152157
limit: int = 20,
153-
offset: int = 0,
154158
) -> list[RunHistoryItem]:
155-
listing = self.query_runs().list_query_runs(limit=limit, offset=offset)
159+
listing = self.query_runs().list_query_runs(limit=limit)
156160
return [
157161
RunHistoryItem(
158162
query_run_id=r.id,
@@ -292,6 +296,18 @@ def _wait_result_ready(
292296
)
293297

294298
def execute_sql(self, sql: str) -> QueryResult:
299+
last_err: BaseException | None = None
300+
for attempt in range(3):
301+
try:
302+
return self._execute_sql_once(sql)
303+
except (ProtocolError, ConnectionResetError, Urllib3HTTPError) as e:
304+
last_err = e
305+
if attempt == 2:
306+
raise
307+
time.sleep(0.2 * (2**attempt))
308+
raise last_err # pragma: no cover
309+
310+
def _execute_sql_once(self, sql: str) -> QueryResult:
295311
q = self._query_api()
296312
try:
297313
raw = q.query(QueryRequest(sql=sql))

hotdata_runtime/http.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""HTTP client defaults for Hotdata SDK :class:`~hotdata.Configuration`."""
2+
3+
from __future__ import annotations
4+
5+
from urllib3.util.retry import Retry
6+
7+
8+
def default_http_retries() -> Retry:
9+
"""Retry transient connection failures (e.g. stale pooled sockets)."""
10+
return Retry(
11+
total=3,
12+
connect=3,
13+
read=3,
14+
backoff_factor=0.2,
15+
status_forcelist=(502, 503, 504),
16+
allowed_methods=frozenset(
17+
["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]
18+
),
19+
)

0 commit comments

Comments
 (0)