-
Notifications
You must be signed in to change notification settings - Fork 117
[471] - Close underlying HTTP Client on closing Connection
#674
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
base: main
Are you sure you want to change the base?
Changes from all commits
4437a2a
30c04a6
4294600
3155211
1143838
68cc822
ef1d9fd
4bb2e4b
734dd06
d00e3c8
000d3a3
cba3da7
2a1f719
1dd40a1
3847aca
d9a4797
ba2a3a9
d1f045e
ea3b0b0
4e66230
bf0a2f6
67020f1
496d7f7
84ec33a
76ce5ce
4452725
d90ac80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
import json | ||
from concurrent.futures import ThreadPoolExecutor | ||
from concurrent.futures import Future | ||
from concurrent.futures import wait | ||
from datetime import datetime, timezone | ||
from typing import List, Dict, Any, Optional, TYPE_CHECKING | ||
from databricks.sql.telemetry.models.event import ( | ||
|
@@ -182,6 +183,7 @@ def __init__( | |
self._user_agent = None | ||
self._events_batch = [] | ||
self._lock = threading.RLock() | ||
self._pending_futures = set() | ||
self._driver_connection_params = None | ||
self._host_url = host_url | ||
self._executor = executor | ||
|
@@ -245,6 +247,9 @@ def _send_telemetry(self, events): | |
timeout=900, | ||
) | ||
|
||
with self._lock: | ||
self._pending_futures.add(future) | ||
|
||
future.add_done_callback( | ||
lambda fut: self._telemetry_request_callback(fut, sent_count=sent_count) | ||
) | ||
|
@@ -303,6 +308,9 @@ def _telemetry_request_callback(self, future, sent_count: int): | |
|
||
except Exception as e: | ||
logger.debug("Telemetry request failed with exception: %s", e) | ||
finally: | ||
with self._lock: | ||
self._pending_futures.discard(future) | ||
|
||
def _export_telemetry_log(self, **telemetry_event_kwargs): | ||
""" | ||
|
@@ -356,10 +364,29 @@ def export_latency_log(self, latency_ms, sql_execution_event, sql_statement_id): | |
) | ||
|
||
def close(self): | ||
"""Flush remaining events before closing""" | ||
logger.debug("Closing TelemetryClient for connection %s", self._session_id_hex) | ||
"""Schedules the client to be closed in the background.""" | ||
logger.debug( | ||
"Scheduling background closure for TelemetryClient of connection %s", | ||
self._session_id_hex, | ||
) | ||
self._executor.submit(self._close_and_wait) | ||
|
||
def _close_and_wait(self): | ||
"""Flush remaining events and wait for them to complete before closing.""" | ||
self._flush() | ||
|
||
with self._lock: | ||
futures_to_wait_on = list(self._pending_futures) | ||
|
||
if futures_to_wait_on: | ||
logger.debug( | ||
"Waiting for %s pending telemetry requests to complete.", | ||
len(futures_to_wait_on), | ||
) | ||
wait(futures_to_wait_on) | ||
|
||
self._http_client.close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great catch! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have merged the updated Thanks for the catch, I didn't realise I'll go ahead and implement this change by adding some state that tracks each future and waits for them to complete before invoking the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we wouldn't want to make the close() synchronous, I was trying this out, but the delay of this being sync is just too much. ideally we would like to perform async close of http_client based on future completion |
||
|
||
|
||
class TelemetryClientFactory: | ||
""" | ||
|
@@ -460,7 +487,6 @@ def initialize_telemetry_client( | |
): | ||
"""Initialize a telemetry client for a specific connection if telemetry is enabled""" | ||
try: | ||
|
||
with TelemetryClientFactory._lock: | ||
TelemetryClientFactory._initialize() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is a session and http client closed together? I feel it is easier to imagine the backend module as a function module for interacting with SQL EXEC. So, with that perspective, there should be minimal state in this and the backend should receive resources by higher level classes and those higher level classes should take care of closing the resources (for example, the connection/cursor should pass a http client to this and close the http client. Same goes for session: cursor maintains the session obtained functionally through this backend and should take care of closing the session). Please let me know if this doesn't look okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the philosophy overall, but I think passing the HTTP client into the backend from a higher-level object is likely infeasible right now since both backends have distinct, custom HTTP clients that expose different methods and are processed differently. Until we unify them, it makes sense to make the backend responsible for it's own HTTP Client instantiation, as a result of which it should be responsible for closing it as well.
Both HTTP Clients do have largely the same functionality, so unifying them (for the most part) could be a separate PR, at which point we can pass in the HTTP client from the
Session
class.Let me know if I should do it in this PR instead.
Note that the pattern of instantiating the required HTTP client in the constructor of the concrete backend is common across Thrift and SEA.