Skip to content

Commit c4595fe

Browse files
committed
fix: include statement IDs in Thrift failure telemetry
Signed-off-by: Ali Abouelazm <aliazm419@gmail.com>
1 parent b1978d4 commit c4595fe

6 files changed

Lines changed: 346 additions & 9 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Release History
22

33
# Unreleased
4+
- Include the available SQL statement ID in Thrift failure telemetry so errors can be correlated with the failed statement.
45
- Add the `escape_catalog_name` connection parameter (default `False`) to opt in to escaping unescaped `_` in metadata catalog names on Thrift and kernel connections. Existing escapes and `%` wildcards are preserved.
56

67
# 4.6.0 (2026-09-24)

‎src/databricks/sql/backend/thrift_backend.py‎

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,19 @@ def _initialize_retry_args(self, kwargs):
303303
)
304304

305305
@staticmethod
306-
def _check_response_for_error(response, host_url=None, detect_reyden=False):
306+
def _get_statement_id(op_handle):
307+
operation_id = getattr(op_handle, "operationId", None)
308+
guid = getattr(operation_id, "guid", None)
309+
return guid_to_hex_id(guid) if guid is not None else None
310+
311+
@staticmethod
312+
def _check_response_for_error(
313+
response,
314+
host_url=None,
315+
detect_reyden=False,
316+
session_id_hex=None,
317+
sql_statement_id=None,
318+
):
307319
if response.status and response.status.statusCode in [
308320
ttypes.TStatusCode.ERROR_STATUS,
309321
ttypes.TStatusCode.INVALID_HANDLE_STATUS,
@@ -323,6 +335,11 @@ def _check_response_for_error(response, host_url=None, detect_reyden=False):
323335
raise DatabaseError(
324336
response.status.errorMessage,
325337
host_url=host_url,
338+
session_id_hex=session_id_hex,
339+
sql_statement_id=ThriftDatabricksClient._get_statement_id(
340+
getattr(response, "operationHandle", None)
341+
)
342+
or sql_statement_id,
326343
)
327344

328345
@staticmethod
@@ -378,6 +395,10 @@ def _handle_request_error(self, error_info, attempt, elapsed):
378395
full_error_info_context,
379396
self._host,
380397
error_info.error,
398+
session_id_hex=self._session_id_hex,
399+
sql_statement_id=self._get_statement_id(
400+
getattr(error_info.request, "operationHandle", None)
401+
),
381402
)
382403
logger.info(network_request_error.message_with_context())
383404

@@ -558,6 +579,10 @@ def attempt_request(attempt):
558579
response,
559580
self._host,
560581
detect_reyden=getattr(method, "__name__", None) == "OpenSession",
582+
session_id_hex=self._session_id_hex,
583+
sql_statement_id=self._get_statement_id(
584+
getattr(request, "operationHandle", None)
585+
),
561586
)
562587
return response
563588

@@ -680,6 +705,8 @@ def _check_command_not_in_error_or_closed_state(
680705
"diagnostic-info": get_operations_resp.diagnosticInfo,
681706
},
682707
host_url=self._host,
708+
session_id_hex=self._session_id_hex,
709+
sql_statement_id=self._get_statement_id(op_handle),
683710
)
684711
else:
685712
raise ServerOperationError(
@@ -690,6 +717,8 @@ def _check_command_not_in_error_or_closed_state(
690717
"diagnostic-info": None,
691718
},
692719
host_url=self._host,
720+
session_id_hex=self._session_id_hex,
721+
sql_statement_id=self._get_statement_id(op_handle),
693722
)
694723
elif get_operations_resp.operationState == ttypes.TOperationState.CLOSED_STATE:
695724
raise DatabaseError(
@@ -701,6 +730,8 @@ def _check_command_not_in_error_or_closed_state(
701730
and guid_to_hex_id(op_handle.operationId.guid)
702731
},
703732
host_url=self._host,
733+
session_id_hex=self._session_id_hex,
734+
sql_statement_id=self._get_statement_id(op_handle),
704735
)
705736

706737
def _poll_for_status(self, op_handle):
@@ -862,6 +893,8 @@ def _results_message_to_execute_response(
862893
]
863894
),
864895
host_url=self._host,
896+
session_id_hex=self._session_id_hex,
897+
sql_statement_id=self._get_statement_id(resp.operationHandle),
865898
)
866899
direct_results = resp.directResults
867900
has_been_closed_server_side = direct_results and direct_results.closeOperation
@@ -1019,27 +1052,40 @@ def get_query_state(self, command_id: CommandId) -> CommandState:
10191052
return state
10201053

10211054
@staticmethod
1022-
def _check_direct_results_for_error(t_spark_direct_results, host_url=None):
1055+
def _check_direct_results_for_error(
1056+
t_spark_direct_results,
1057+
host_url=None,
1058+
session_id_hex=None,
1059+
sql_statement_id=None,
1060+
):
10231061
if t_spark_direct_results:
10241062
if t_spark_direct_results.operationStatus:
10251063
ThriftDatabricksClient._check_response_for_error(
10261064
t_spark_direct_results.operationStatus,
10271065
host_url,
1066+
session_id_hex=session_id_hex,
1067+
sql_statement_id=sql_statement_id,
10281068
)
10291069
if t_spark_direct_results.resultSetMetadata:
10301070
ThriftDatabricksClient._check_response_for_error(
10311071
t_spark_direct_results.resultSetMetadata,
10321072
host_url,
1073+
session_id_hex=session_id_hex,
1074+
sql_statement_id=sql_statement_id,
10331075
)
10341076
if t_spark_direct_results.resultSet:
10351077
ThriftDatabricksClient._check_response_for_error(
10361078
t_spark_direct_results.resultSet,
10371079
host_url,
1080+
session_id_hex=session_id_hex,
1081+
sql_statement_id=sql_statement_id,
10381082
)
10391083
if t_spark_direct_results.closeOperation:
10401084
ThriftDatabricksClient._check_response_for_error(
10411085
t_spark_direct_results.closeOperation,
10421086
host_url,
1087+
session_id_hex=session_id_hex,
1088+
sql_statement_id=sql_statement_id,
10431089
)
10441090

10451091
def execute_command(
@@ -1327,7 +1373,12 @@ def _handle_execute_response(self, resp, cursor):
13271373
raise ValueError(f"Invalid Thrift handle: {resp.operationHandle}")
13281374

13291375
cursor.active_command_id = command_id
1330-
self._check_direct_results_for_error(resp.directResults, self._host)
1376+
self._check_direct_results_for_error(
1377+
resp.directResults,
1378+
self._host,
1379+
session_id_hex=self._session_id_hex,
1380+
sql_statement_id=command_id.to_hex_guid(),
1381+
)
13311382

13321383
final_operation_state, final_status_resp = self._wait_until_command_done(
13331384
resp.operationHandle,
@@ -1351,7 +1402,12 @@ def _handle_execute_response_async(self, resp, cursor):
13511402
raise ValueError(f"Invalid Thrift handle: {resp.operationHandle}")
13521403

13531404
cursor.active_command_id = command_id
1354-
self._check_direct_results_for_error(resp.directResults, self._host)
1405+
self._check_direct_results_for_error(
1406+
resp.directResults,
1407+
self._host,
1408+
session_id_hex=self._session_id_hex,
1409+
sql_statement_id=command_id.to_hex_guid(),
1410+
)
13551411

13561412
def fetch_results(
13571413
self,
@@ -1390,6 +1446,8 @@ def fetch_results(
13901446
expected_row_start_offset, resp.results.startRowOffset
13911447
),
13921448
host_url=self._host,
1449+
session_id_hex=self._session_id_hex,
1450+
sql_statement_id=command_id.to_hex_guid(),
13931451
)
13941452

13951453
queue = ThriftResultSetQueueFactory.build_queue(

‎src/databricks/sql/exc.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def __init__(
1919
host_url=None,
2020
*args,
2121
session_id_hex=None,
22+
sql_statement_id=None,
2223
**kwargs,
2324
):
2425
super().__init__(message, *args, **kwargs)
@@ -33,7 +34,10 @@ def __init__(
3334
host_url=host_url
3435
)
3536
telemetry_client.export_failure_log(
36-
error_name, self.message, session_id=session_id_hex
37+
error_name,
38+
self.message,
39+
session_id=session_id_hex,
40+
sql_statement_id=sql_statement_id,
3741
)
3842

3943
def __str__(self):

‎src/databricks/sql/telemetry/telemetry_client.py‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ def export_initial_telemetry_log(
161161
):
162162
pass
163163

164-
def export_failure_log(self, error_name, error_message, session_id=None):
164+
def export_failure_log(
165+
self, error_name, error_message, session_id=None, sql_statement_id=None
166+
):
165167
pass
166168

167169
def export_latency_log(
@@ -414,9 +416,15 @@ def export_initial_telemetry_log(
414416
self._user_agent = user_agent
415417
self._export_telemetry_log(session_id=session_id)
416418

417-
def export_failure_log(self, error_name, error_message, session_id=None):
419+
def export_failure_log(
420+
self, error_name, error_message, session_id=None, sql_statement_id=None
421+
):
418422
error_info = DriverErrorInfo(error_name=error_name, stack_trace=error_message)
419-
self._export_telemetry_log(session_id=session_id, error_info=error_info)
423+
self._export_telemetry_log(
424+
session_id=session_id,
425+
sql_statement_id=sql_statement_id,
426+
error_info=error_info,
427+
)
420428

421429
def export_latency_log(
422430
self, latency_ms, sql_execution_event, sql_statement_id, session_id=None

‎src/databricks/sql/telemetry/utils.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def export_initial_telemetry_log(self, driver_connection_params, user_agent):
1919
pass
2020

2121
@abstractmethod
22-
def export_failure_log(self, error_name, error_message):
22+
def export_failure_log(
23+
self, error_name, error_message, session_id=None, sql_statement_id=None
24+
):
2325
logger.debug("subclass must implement export_failure_log")
2426
pass
2527

0 commit comments

Comments
 (0)