-
Notifications
You must be signed in to change notification settings - Fork 113
Add functionality for export of latency logs via telemetry #608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
90 commits
Select commit
Hold shift + click to select a range
65a75f4
added functionality for export of failure logs
saishreeeee 5305308
changed logger.error to logger.debug in exc.py
saishreeeee ba83c33
Fix telemetry loss during Python shutdown
saishreeeee 131db92
unit tests for export_failure_log
saishreeeee 3abc40d
try-catch blocks to make telemetry failures non-blocking for connecto…
saishreeeee ffa4787
removed redundant try/catch blocks, added try/catch block to initiali…
saishreeeee cc077f3
skip null fields in telemetry request
saishreeeee 2c6fd44
removed dup import, renamed func, changed a filter_null_values to lamda
saishreeeee 89540a1
removed unnecassary class variable and a redundant try/except block
saishreeeee 52a1152
public functions defined at interface level
saishreeeee 3dcdcfa
changed export_event and flush to private functions
saishreeeee b2714c9
formatting
saishreeeee 377a87b
changed connection_uuid to thread local in thrift backend
saishreeeee c9376b8
made errors more specific
saishreeeee bbfadf2
revert change to connection_uuid
saishreeeee 9bce26b
reverting change in close in telemetry client
saishreeeee ef4514d
JsonSerializableMixin
saishreeeee 8924835
isdataclass check in JsonSerializableMixin
saishreeeee 65361e7
convert TelemetryClientFactory to module-level functions, replace Noo…
saishreeeee 1722a77
renamed connection_uuid as session_id_hex
saishreeeee e841434
added NotImplementedError to abstract class, added unit tests
saishreeeee 2f89266
formatting
saishreeeee 5564bbb
added PEP-249 link, changed NoopTelemetryClient implementation
saishreeeee 1e4e8cf
removed unused import
saishreeeee 55b29bc
made telemetry client close a module-level function
saishreeeee 93bf170
unit tests verbose
saishreeeee 45f5ccf
debug logs in unit tests
saishreeeee 8ff1c1f
debug logs in unit tests
saishreeeee 8bdd324
removed ABC from mixin, added try/catch block around executor shutdown
saishreeeee f99f7ea
checking stuff
saishreeeee b972c8a
finding out
saishreeeee 7ca3636
finding out more
saishreeeee 0ac8ed2
more more finding out more nice
saishreeeee c457a09
locks are useless anyways
saishreeeee 5f07a84
haha
saishreeeee 1115e25
normal
saishreeeee de1ed87
:= looks like walrus horizontally
saishreeeee 554aeaf
one more
saishreeeee fffac5f
walrus again
saishreeeee b77208a
old stuff without walrus seems to fail
saishreeeee 733c288
manually do the walrussing
saishreeeee ca8b958
change 3.13t, v2
saishreeeee 3eabac9
formatting, added walrus
saishreeeee fb9ef43
formatting
saishreeeee 1e795aa
removed walrus, removed test before stalling test
saishreeeee 2c293a5
changed order of stalling test
saishreeeee d237255
removed debugging, added TelemetryClientFactory
saishreeeee f101b19
remove more debugging
saishreeeee a094659
latency logs funcitionality
saishreeeee 695a07d
merge
saishreeeee fc918d6
fixed type of return value in get_session_id_hex() in thrift backend
saishreeeee d7c75d7
debug on TelemetryClientFactory lock
saishreeeee b6b0f89
formatting
saishreeeee 50a1206
type notation for _waiters
saishreeeee 39a0530
called connection.close() in test_arraysize_buffer_size_passthrough
saishreeeee 413427f
run all unit tests
saishreeeee 6b1d1b8
more debugging
saishreeeee 8f5e5ba
removed the connection.close() from that test, put debug statement be…
saishreeeee 2dc00ba
more debug
saishreeeee 1ff03d4
more more more
saishreeeee 6ff07c8
why
saishreeeee 395049a
whywhy
saishreeeee 4466821
thread name
saishreeeee 34b63e4
added teardown to all tests except finalizer test (gc collect)
saishreeeee 49082fb
added the get_attribute functions to the classes
saishreeeee ed1db9d
removed tearDown, added connection.close() to first test
saishreeeee 9fa5a89
finally
saishreeeee 14433c4
remove debugging
saishreeeee ef4ca13
added test for export_latency_log, made mock of thrift backend with r…
saishreeeee 152e0da
Merge branch 'telemetry' into PECOBLR-554
saishreeeee b5bf165
added multi threaded tests
saishreeeee 307a8cc
formatting
saishreeeee 0fd46d4
added TelemetryExtractor, removed multithreaded tests
saishreeeee f6f50b2
formatting
saishreeeee 1163ebe
fixes in test
saishreeeee 4b6ace0
fix in telemetry extractor
saishreeeee 7171718
Merge branch 'telemetry' into PECOBLR-554
saishreeeee a059a03
Merge branch 'telemetry' into PECOBLR-554
saishreeeee 4d56141
added doc strings to latency_logger, abstracted export_telemetry_log
saishreeeee 27295c2
statement type, unit test fix
saishreeeee b558bc8
unit test fix
saishreeeee 01853bc
statement type changes
saishreeeee 45f74d0
test_fetches fix
saishreeeee 149d4a8
added mocks to resolve the errors caused by log_latency decorator in …
saishreeeee e031663
removed function in test_fetches cuz it is only used once
saishreeeee 142b9a8
added _safe_call which returns None in case of errors in the get func…
saishreeeee 2a26965
removed the changes in test_client and test_fetches
saishreeeee ae90dee
removed the changes in test_fetches
saishreeeee acc9904
test_telemetry
saishreeeee a847122
removed test
saishreeeee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
import time | ||
import functools | ||
from typing import Optional | ||
from databricks.sql.telemetry.telemetry_client import TelemetryClientFactory | ||
from databricks.sql.telemetry.models.event import ( | ||
SqlExecutionEvent, | ||
) | ||
from databricks.sql.telemetry.models.enums import ExecutionResultFormat, StatementType | ||
from databricks.sql.utils import ColumnQueue, CloudFetchQueue, ArrowQueue | ||
from uuid import UUID | ||
|
||
|
||
class TelemetryExtractor: | ||
""" | ||
Base class for extracting telemetry information from various object types. | ||
|
||
This class serves as a proxy that delegates attribute access to the wrapped object | ||
while providing a common interface for extracting telemetry-related data. | ||
""" | ||
|
||
def __init__(self, obj): | ||
""" | ||
Initialize the extractor with an object to wrap. | ||
|
||
Args: | ||
obj: The object to extract telemetry information from. | ||
""" | ||
self._obj = obj | ||
|
||
def __getattr__(self, name): | ||
""" | ||
Delegate attribute access to the wrapped object. | ||
|
||
Args: | ||
name (str): The name of the attribute to access. | ||
|
||
Returns: | ||
The attribute value from the wrapped object. | ||
""" | ||
return getattr(self._obj, name) | ||
|
||
def get_session_id_hex(self): | ||
pass | ||
|
||
def get_statement_id(self): | ||
pass | ||
|
||
def get_is_compressed(self): | ||
pass | ||
|
||
def get_execution_result(self): | ||
pass | ||
|
||
def get_retry_count(self): | ||
pass | ||
|
||
|
||
class CursorExtractor(TelemetryExtractor): | ||
""" | ||
Telemetry extractor specialized for Cursor objects. | ||
|
||
Extracts telemetry information from database cursor objects, including | ||
statement IDs, session information, compression settings, and result formats. | ||
""" | ||
|
||
def get_statement_id(self) -> Optional[str]: | ||
return self.query_id | ||
|
||
def get_session_id_hex(self) -> Optional[str]: | ||
return self.connection.get_session_id_hex() | ||
|
||
def get_is_compressed(self) -> bool: | ||
return self.connection.lz4_compression | ||
|
||
def get_execution_result(self) -> ExecutionResultFormat: | ||
if self.active_result_set is None: | ||
return ExecutionResultFormat.FORMAT_UNSPECIFIED | ||
|
||
saishreeeee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if isinstance(self.active_result_set.results, ColumnQueue): | ||
return ExecutionResultFormat.COLUMNAR_INLINE | ||
elif isinstance(self.active_result_set.results, CloudFetchQueue): | ||
return ExecutionResultFormat.EXTERNAL_LINKS | ||
elif isinstance(self.active_result_set.results, ArrowQueue): | ||
return ExecutionResultFormat.INLINE_ARROW | ||
return ExecutionResultFormat.FORMAT_UNSPECIFIED | ||
|
||
def get_retry_count(self) -> int: | ||
if ( | ||
hasattr(self.thrift_backend, "retry_policy") | ||
and self.thrift_backend.retry_policy | ||
): | ||
return len(self.thrift_backend.retry_policy.history) | ||
return 0 | ||
|
||
|
||
class ResultSetExtractor(TelemetryExtractor): | ||
""" | ||
Telemetry extractor specialized for ResultSet objects. | ||
|
||
Extracts telemetry information from database result set objects, including | ||
operation IDs, session information, compression settings, and result formats. | ||
""" | ||
|
||
def get_statement_id(self) -> Optional[str]: | ||
if self.command_id: | ||
return str(UUID(bytes=self.command_id.operationId.guid)) | ||
return None | ||
|
||
def get_session_id_hex(self) -> Optional[str]: | ||
return self.connection.get_session_id_hex() | ||
|
||
def get_is_compressed(self) -> bool: | ||
return self.lz4_compressed | ||
|
||
def get_execution_result(self) -> ExecutionResultFormat: | ||
if isinstance(self.results, ColumnQueue): | ||
return ExecutionResultFormat.COLUMNAR_INLINE | ||
elif isinstance(self.results, CloudFetchQueue): | ||
return ExecutionResultFormat.EXTERNAL_LINKS | ||
elif isinstance(self.results, ArrowQueue): | ||
return ExecutionResultFormat.INLINE_ARROW | ||
return ExecutionResultFormat.FORMAT_UNSPECIFIED | ||
saishreeeee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_retry_count(self) -> int: | ||
if ( | ||
hasattr(self.thrift_backend, "retry_policy") | ||
and self.thrift_backend.retry_policy | ||
): | ||
return len(self.thrift_backend.retry_policy.history) | ||
return 0 | ||
|
||
|
||
def get_extractor(obj): | ||
""" | ||
Factory function to create the appropriate telemetry extractor for an object. | ||
|
||
Determines the object type and returns the corresponding specialized extractor | ||
that can extract telemetry information from that object type. | ||
|
||
Args: | ||
obj: The object to create an extractor for. Can be a Cursor, ResultSet, | ||
or any other object. | ||
|
||
Returns: | ||
TelemetryExtractor: A specialized extractor instance: | ||
- CursorExtractor for Cursor objects | ||
- ResultSetExtractor for ResultSet objects | ||
- Throws an NotImplementedError for all other objects | ||
""" | ||
if obj.__class__.__name__ == "Cursor": | ||
saishreeeee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return CursorExtractor(obj) | ||
elif obj.__class__.__name__ == "ResultSet": | ||
return ResultSetExtractor(obj) | ||
else: | ||
raise NotImplementedError(f"No extractor found for {obj.__class__.__name__}") | ||
|
||
|
||
def log_latency(statement_type: StatementType = StatementType.NONE): | ||
""" | ||
Decorator for logging execution latency and telemetry information. | ||
|
||
This decorator measures the execution time of a method and sends telemetry | ||
data about the operation, including latency, statement information, and | ||
execution context. | ||
|
||
The decorator automatically: | ||
- Measures execution time using high-precision performance counters | ||
- Extracts telemetry information from the method's object (self) | ||
- Creates a SqlExecutionEvent with execution details | ||
- Sends the telemetry data asynchronously via TelemetryClient | ||
|
||
Args: | ||
statement_type (StatementType): The type of SQL statement being executed. | ||
|
||
Usage: | ||
@log_latency(StatementType.SQL) | ||
def execute(self, query): | ||
# Method implementation | ||
pass | ||
|
||
Returns: | ||
function: A decorator that wraps methods to add latency logging. | ||
|
||
Note: | ||
The wrapped method's object (self) must be compatible with the | ||
telemetry extractor system (e.g., Cursor or ResultSet objects). | ||
""" | ||
|
||
def decorator(func): | ||
@functools.wraps(func) | ||
def wrapper(self, *args, **kwargs): | ||
saishreeeee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
start_time = time.perf_counter() | ||
result = None | ||
try: | ||
result = func(self, *args, **kwargs) | ||
return result | ||
finally: | ||
|
||
def _safe_call(func_to_call): | ||
"""Calls a function and returns a default value on any exception.""" | ||
try: | ||
return func_to_call() | ||
except Exception: | ||
return None | ||
|
||
end_time = time.perf_counter() | ||
duration_ms = int((end_time - start_time) * 1000) | ||
|
||
extractor = get_extractor(self) | ||
session_id_hex = _safe_call(extractor.get_session_id_hex) | ||
statement_id = _safe_call(extractor.get_statement_id) | ||
|
||
sql_exec_event = SqlExecutionEvent( | ||
statement_type=statement_type, | ||
saishreeeee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
is_compressed=_safe_call(extractor.get_is_compressed), | ||
execution_result=_safe_call(extractor.get_execution_result), | ||
retry_count=_safe_call(extractor.get_retry_count), | ||
) | ||
|
||
telemetry_client = TelemetryClientFactory.get_telemetry_client( | ||
session_id_hex | ||
) | ||
telemetry_client.export_latency_log( | ||
latency_ms=duration_ms, | ||
sql_execution_event=sql_exec_event, | ||
sql_statement_id=statement_id, | ||
) | ||
|
||
return wrapper | ||
|
||
return decorator |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.