Skip to content

Commit e6106da

Browse files
committed
Revert "Enhance Cursor close handling and context manager exception management to prevent server side resource leaks (#554)"
This reverts commit edfb283.
1 parent c123af3 commit e6106da

File tree

3 files changed

+6
-129
lines changed

3 files changed

+6
-129
lines changed

src/databricks/sql/client.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,7 @@ def __enter__(self) -> "Connection":
321321
return self
322322

323323
def __exit__(self, exc_type, exc_value, traceback):
324-
try:
325-
self.close()
326-
except BaseException as e:
327-
logger.warning(f"Exception during connection close in __exit__: {e}")
328-
if exc_type is None:
329-
raise
330-
return False
324+
self.close()
331325

332326
def __del__(self):
333327
if self.open:
@@ -468,14 +462,7 @@ def __enter__(self) -> "Cursor":
468462
return self
469463

470464
def __exit__(self, exc_type, exc_value, traceback):
471-
try:
472-
logger.debug("Cursor context manager exiting, calling close()")
473-
self.close()
474-
except BaseException as e:
475-
logger.warning(f"Exception during cursor close in __exit__: {e}")
476-
if exc_type is None:
477-
raise
478-
return False
465+
self.close()
479466

480467
def __iter__(self):
481468
if self.active_result_set:
@@ -1185,21 +1172,7 @@ def cancel(self) -> None:
11851172
def close(self) -> None:
11861173
"""Close cursor"""
11871174
self.open = False
1188-
1189-
# Close active operation handle if it exists
1190-
if self.active_op_handle:
1191-
try:
1192-
self.thrift_backend.close_command(self.active_op_handle)
1193-
except RequestError as e:
1194-
if isinstance(e.args[1], CursorAlreadyClosedError):
1195-
logger.info("Operation was canceled by a prior request")
1196-
else:
1197-
logging.warning(f"Error closing operation handle: {e}")
1198-
except Exception as e:
1199-
logging.warning(f"Error closing operation handle: {e}")
1200-
finally:
1201-
self.active_op_handle = None
1202-
1175+
self.active_op_handle = None
12031176
if self.active_result_set:
12041177
self._close_and_clear_active_result_set()
12051178

tests/e2e/test_driver.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
from tests.e2e.common.uc_volume_tests import PySQLUCVolumeTestSuiteMixin
5252

53-
from databricks.sql.exc import SessionAlreadyClosedError, CursorAlreadyClosedError
53+
from databricks.sql.exc import SessionAlreadyClosedError
5454

5555
log = logging.getLogger(__name__)
5656

@@ -820,6 +820,7 @@ def test_close_connection_closes_cursors(self):
820820
ars = cursor.active_result_set
821821

822822
# We must manually run this check because thrift_backend always forces `has_been_closed_server_side` to True
823+
823824
# Cursor op state should be open before connection is closed
824825
status_request = ttypes.TGetOperationStatusReq(
825826
operationHandle=ars.command_id, getProgressUpdate=False
@@ -846,6 +847,7 @@ def test_closing_a_closed_connection_doesnt_fail(self, caplog):
846847
with self.connection() as conn:
847848
# First .close() call is explicit here
848849
conn.close()
850+
849851
assert "Session appears to have been closed already" in caplog.text
850852

851853
conn = None

tests/unit/test_client.py

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import databricks.sql
2222
import databricks.sql.client as client
2323
from databricks.sql import InterfaceError, DatabaseError, Error, NotSupportedError
24-
from databricks.sql.exc import RequestError, CursorAlreadyClosedError
2524
from databricks.sql.types import Row
2625

2726
from databricks.sql.utils import ExecuteResponse
@@ -342,15 +341,6 @@ def test_context_manager_closes_cursor(self):
342341
cursor.close = mock_close
343342
mock_close.assert_called_once_with()
344343

345-
cursor = client.Cursor(Mock(), Mock())
346-
cursor.close = Mock()
347-
try:
348-
with self.assertRaises(KeyboardInterrupt):
349-
with cursor:
350-
raise KeyboardInterrupt("Simulated interrupt")
351-
finally:
352-
cursor.close.assert_called()
353-
354344
@patch("%s.client.ThriftBackend" % PACKAGE_NAME)
355345
def test_context_manager_closes_connection(self, mock_client_class):
356346
instance = mock_client_class.return_value
@@ -366,15 +356,6 @@ def test_context_manager_closes_connection(self, mock_client_class):
366356
close_session_id = instance.close_session.call_args[0][0].sessionId
367357
self.assertEqual(close_session_id, b"\x22")
368358

369-
connection = databricks.sql.connect(**self.DUMMY_CONNECTION_ARGS)
370-
connection.close = Mock()
371-
try:
372-
with self.assertRaises(KeyboardInterrupt):
373-
with connection:
374-
raise KeyboardInterrupt("Simulated interrupt")
375-
finally:
376-
connection.close.assert_called()
377-
378359
def dict_product(self, dicts):
379360
"""
380361
Generate cartesion product of values in input dictionary, outputting a dictionary
@@ -753,42 +734,6 @@ def test_access_current_query_id(self):
753734
cursor.close()
754735
self.assertIsNone(cursor.query_id)
755736

756-
def test_cursor_close_handles_exception(self):
757-
"""Test that Cursor.close() handles exceptions from close_command properly."""
758-
mock_backend = Mock()
759-
mock_connection = Mock()
760-
mock_op_handle = Mock()
761-
762-
mock_backend.close_command.side_effect = Exception("Test error")
763-
764-
cursor = client.Cursor(mock_connection, mock_backend)
765-
cursor.active_op_handle = mock_op_handle
766-
767-
cursor.close()
768-
769-
mock_backend.close_command.assert_called_once_with(mock_op_handle)
770-
771-
self.assertIsNone(cursor.active_op_handle)
772-
773-
self.assertFalse(cursor.open)
774-
775-
def test_cursor_context_manager_handles_exit_exception(self):
776-
"""Test that cursor's context manager handles exceptions during __exit__."""
777-
mock_backend = Mock()
778-
mock_connection = Mock()
779-
780-
cursor = client.Cursor(mock_connection, mock_backend)
781-
original_close = cursor.close
782-
cursor.close = Mock(side_effect=Exception("Test error during close"))
783-
784-
try:
785-
with cursor:
786-
raise ValueError("Test error inside context")
787-
except ValueError:
788-
pass
789-
790-
cursor.close.assert_called_once()
791-
792737
def test_connection_close_handles_cursor_close_exception(self):
793738
"""Test that _close handles exceptions from cursor.close() properly."""
794739
cursors_closed = []
@@ -824,49 +769,6 @@ def mock_close_normal():
824769
cursors_closed, [1, 2], "Both cursors should have close called"
825770
)
826771

827-
def test_resultset_close_handles_cursor_already_closed_error(self):
828-
"""Test that ResultSet.close() handles CursorAlreadyClosedError properly."""
829-
result_set = client.ResultSet.__new__(client.ResultSet)
830-
result_set.thrift_backend = Mock()
831-
result_set.thrift_backend.CLOSED_OP_STATE = "CLOSED"
832-
result_set.connection = Mock()
833-
result_set.connection.open = True
834-
result_set.op_state = "RUNNING"
835-
result_set.has_been_closed_server_side = False
836-
result_set.command_id = Mock()
837-
838-
class MockRequestError(Exception):
839-
def __init__(self):
840-
self.args = ["Error message", CursorAlreadyClosedError()]
841-
842-
result_set.thrift_backend.close_command.side_effect = MockRequestError()
843-
844-
original_close = client.ResultSet.close
845-
try:
846-
try:
847-
if (
848-
result_set.op_state != result_set.thrift_backend.CLOSED_OP_STATE
849-
and not result_set.has_been_closed_server_side
850-
and result_set.connection.open
851-
):
852-
result_set.thrift_backend.close_command(result_set.command_id)
853-
except MockRequestError as e:
854-
if isinstance(e.args[1], CursorAlreadyClosedError):
855-
pass
856-
finally:
857-
result_set.has_been_closed_server_side = True
858-
result_set.op_state = result_set.thrift_backend.CLOSED_OP_STATE
859-
860-
result_set.thrift_backend.close_command.assert_called_once_with(
861-
result_set.command_id
862-
)
863-
864-
assert result_set.has_been_closed_server_side is True
865-
866-
assert result_set.op_state == result_set.thrift_backend.CLOSED_OP_STATE
867-
finally:
868-
pass
869-
870772

871773
if __name__ == "__main__":
872774
suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])

0 commit comments

Comments
 (0)