Cursor.close() leaks server-side handle for async commands that were never fetched

Đang mở Phù hợp với người mới
#791 5 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
72/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Ít trao đổi
Công nghệ
python, sql
Lĩnh vực
backend, databases

Hướng nghiên cứu

Bắt đầu trong src/databricks/sql/client.py tại Cursor.close(), các dòng 1713-1718, sau đó kiểm tra các triển khai close_command cho các backend Thrift, SEA và kernel. Tái hiện một lần gửi không đồng bộ không bao giờ gọi get_execution_result(), đóng cursor và xác minh rằng handle phía máy chủ được giải phóng mà không làm ảnh hưởng đến việc dọn dẹp các kết quả đang hoạt động hoặc hành vi của backend.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

bug engineer-bot

Summary

Cursor.close() does not free the server-side statement handle for async-submitted commands when get_execution_result() was never called. The handle leaks until the session closes (where backend-specific sweeps may or may not catch it).

Repro

import databricks.sql

with databricks.sql.connect(...) as conn:
    cur = conn.cursor()
    cur.execute("SELECT * FROM huge_table", async_op=True)
    # User decides not to wait for the result.
    cur.close()
    # Server-side ExecutedAsyncStatement still tracked until conn.close().

Root cause

Cursor.close() at src/databricks/sql/client.py:1713-1718 only nulls active_command_id and closes active_result_set — but for the async-submit-without-fetch case, active_result_set is None, so backend.close_command(active_command_id) is never invoked.

def close(self) -> None:
    """Close cursor"""
    self.open = False
    self.active_command_id = None
    if self.active_result_set:
        self._close_and_clear_active_result_set()

Scope

Affects all three backends: Thrift, SEA, and the new kernel backend. Each one tracks the async handle differently (Thrift via Thrift-server-side state; SEA via the SEA statement; kernel via _async_handles dict), but the shared Cursor.close() path doesn't notify any of them about the unfetched async handle.

Suggested fix

Single change in shared Cursor.close():

def close(self) -> None:
    self.open = False
    if self.active_result_set:
        self._close_and_clear_active_result_set()
    elif self.active_command_id is not None:
        # async submission that never had get_execution_result called —
        # the result-set close path didn't fire, so issue an explicit
        # close_command to free the server-side handle.
        try:
            self.backend.close_command(self.active_command_id)
        except Exception as exc:
            logger.warning("close_command on cursor close failed: %s", exc)
    self.active_command_id = None

Safe across all backends: Thrift's close_command sends TCloseOperation (valid call), SEA's sends a cancel/close, kernel's is already tolerant of unknown ids (no-op).

Surfaced by

Review of PR #787 (kernel backend). Documented as P0 #1 in https://github.com/databricks/databricks-sql-python/pull/787#issuecomment-4475165482 — but the leak is pre-existing on Thrift and SEA too, not specific to the kernel backend.

Ngôn ngữ chính
Python
Star
233
Fork
152
Merge trung bình
21 giờ 5 phút
Pull request đã merge (30 ngày)
10

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của databricks/databricks-sql-python

Tất cả issue của databricks/databricks-sql-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.