Skip to content

Commit ae59e2c

Browse files
committed
Disable Python telemetry for kernel connections
1 parent e550cb7 commit ae59e2c

6 files changed

Lines changed: 178 additions & 2 deletions

File tree

src/databricks/sql/backend/kernel/client.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
NotSupportedError,
4848
ProgrammingError,
4949
)
50+
from databricks.sql.telemetry.telemetry_client import TelemetryHelper
5051
from databricks.sql.thrift_api.TCLIService import ttypes
5152

5253
if TYPE_CHECKING:
@@ -165,6 +166,34 @@ def _is_staging_statement(operation: str) -> bool:
165166
return verb in _STAGING_VERBS
166167

167168

169+
def _kernel_telemetry_kwargs(options: Dict[str, Any]) -> Dict[str, Any]:
170+
"""Build phase-7 telemetry/system kwargs for ``databricks_sql_kernel.Session``."""
171+
system = TelemetryHelper.get_driver_system_configuration()
172+
out: Dict[str, Any] = {
173+
"driver_name": system.driver_name,
174+
"driver_version": system.driver_version,
175+
"runtime_name": system.runtime_name,
176+
"runtime_version": system.runtime_version,
177+
"runtime_vendor": system.runtime_vendor,
178+
"os_name": system.os_name,
179+
"os_version": system.os_version,
180+
"os_arch": system.os_arch,
181+
"client_app_name": system.client_app_name,
182+
"locale_name": system.locale_name,
183+
"char_set_encoding": system.char_set_encoding,
184+
# The Python telemetry model does not currently track process
185+
# name; omit it and let the kernel fill what it can derive.
186+
"process_name": None,
187+
"telemetry_enabled": bool(
188+
options.get("force_enable_telemetry")
189+
or options.get("enable_telemetry", True)
190+
),
191+
}
192+
if options.get("telemetry_batch_size") is not None:
193+
out["telemetry_batch_size"] = options["telemetry_batch_size"]
194+
return out
195+
196+
168197
# ─── Client ─────────────────────────────────────────────────────────────────
169198

170199

@@ -217,6 +246,9 @@ def __init__(
217246
# to the kernel ``Session``'s ``retry_*`` kwargs in
218247
# ``open_session`` via ``_kernel_retry_kwargs``.
219248
self._retry_options = kwargs.get("retry_options") or {}
249+
# Kernel telemetry phase 7 adds binding/runtime identity and
250+
# telemetry config kwargs directly to ``databricks_sql_kernel.Session``.
251+
self._telemetry_options = kwargs.get("telemetry_options") or {}
220252
self._catalog = catalog
221253
self._schema = schema
222254
# ``_use_arrow_native_complex_types`` is the connector-side
@@ -316,6 +348,7 @@ def open_session(
316348
# Translate the connector's ``_retry_*`` kwargs into the
317349
# kernel's ``retry_*`` kwargs. Empty when at defaults.
318350
retry_kwargs = _kernel_retry_kwargs(self._retry_options)
351+
telemetry_kwargs = _kernel_telemetry_kwargs(self._telemetry_options)
319352
# Forward caller / connector HTTP headers. The kernel applies
320353
# them on every request; a caller ``User-Agent`` is appended
321354
# to the kernel's base UA. Only pass the kwarg when there's
@@ -358,6 +391,7 @@ def open_session(
358391
**auth_kwargs,
359392
**tls_kwargs,
360393
**retry_kwargs,
394+
**telemetry_kwargs,
361395
**http_headers_kwargs,
362396
)
363397
except Exception as exc:

src/databricks/sql/session.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,19 @@ def _create_backend(
204204
"_retry_stop_after_attempts_duration"
205205
),
206206
}
207+
# Forward the binding/runtime identity and telemetry knobs
208+
# added by kernel telemetry phase 7. Python-side telemetry
209+
# still owns feature-flag evaluation and event export for the
210+
# Thrift/SEA paths; the kernel path needs the same driver
211+
# identity at Session construction time so kernel-owned
212+
# telemetry can populate its system configuration.
213+
kernel_telemetry_options = {
214+
"enable_telemetry": kwargs.get("enable_telemetry", True),
215+
"force_enable_telemetry": kwargs.get(
216+
"force_enable_telemetry", False
217+
),
218+
"telemetry_batch_size": kwargs.get("telemetry_batch_size"),
219+
}
207220
return KernelDatabricksClient(
208221
server_hostname=server_hostname,
209222
http_path=http_path,
@@ -216,6 +229,7 @@ def _create_backend(
216229
_use_arrow_native_complex_types=_use_arrow_native_complex_types,
217230
auth_options=kernel_auth_options,
218231
retry_options=kernel_retry_options,
232+
telemetry_options=kernel_telemetry_options,
219233
)
220234

221235
databricks_client_class: Type[DatabricksClient]

src/databricks/sql/telemetry/telemetry_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ def get_auth_flow(auth_provider):
122122

123123
@staticmethod
124124
def is_telemetry_enabled(connection: "Connection") -> bool:
125+
if getattr(connection.session, "use_kernel", False) is True:
126+
return False
127+
125128
# Fast path: force enabled - skip feature flag fetch entirely
126129
if connection.force_enable_telemetry:
127130
return True

tests/unit/test_kernel_client.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,65 @@ def fake_session(**kw):
344344
assert captured.get("complex_types_as_json") is expected_flag
345345

346346

347+
def test_open_session_passes_phase_7_telemetry_kwargs_to_kernel(monkeypatch):
348+
"""Kernel telemetry phase 7 added binding/runtime identity and
349+
telemetry config kwargs to ``databricks_sql_kernel.Session``."""
350+
captured = {}
351+
352+
def fake_session(**kw):
353+
captured.update(kw)
354+
sess = MagicMock()
355+
sess.session_id = "sess-id"
356+
return sess
357+
358+
monkeypatch.setattr(kernel_client._kernel, "Session", fake_session)
359+
monkeypatch.setattr(
360+
kernel_client.TelemetryHelper,
361+
"get_driver_system_configuration",
362+
lambda: types.SimpleNamespace(
363+
driver_name="Databricks SQL Python Connector",
364+
driver_version="1.2.3",
365+
runtime_name="Python 3.12.0",
366+
runtime_version="3.12.0",
367+
runtime_vendor="CPython",
368+
os_name="Linux",
369+
os_version="6.1",
370+
os_arch="x86_64",
371+
client_app_name=None,
372+
locale_name="en_US",
373+
char_set_encoding="utf-8",
374+
),
375+
)
376+
377+
c = kernel_client.KernelDatabricksClient(
378+
server_hostname="example.cloud.databricks.com",
379+
http_path="/sql/1.0/warehouses/abc",
380+
auth_provider=AccessTokenAuthProvider("dapi-test"),
381+
ssl_options=None,
382+
telemetry_options={
383+
"enable_telemetry": True,
384+
"force_enable_telemetry": False,
385+
"telemetry_batch_size": 17,
386+
},
387+
)
388+
c.open_session(session_configuration=None, catalog=None, schema=None)
389+
390+
assert captured["driver_name"] == "Databricks SQL Python Connector"
391+
assert captured["driver_version"] == "1.2.3"
392+
assert captured["runtime_name"] == "Python 3.12.0"
393+
assert captured["runtime_version"] == "3.12.0"
394+
assert captured["runtime_vendor"] == "CPython"
395+
assert captured["os_name"] == "Linux"
396+
assert captured["os_version"] == "6.1"
397+
assert captured["os_arch"] == "x86_64"
398+
assert captured["client_app_name"] is None
399+
assert captured["locale_name"] == "en_US"
400+
assert captured["char_set_encoding"] == "utf-8"
401+
assert captured["process_name"] is None
402+
assert captured["telemetry_enabled"] is True
403+
assert captured["telemetry_batch_size"] == 17
404+
405+
347406
def test_execute_command_forwards_parameters_to_bind_param():
348407
"""``execute_command(parameters=[...])`` routes each parameter
349408
through ``bind_tspark_params`` onto the kernel statement before

tests/unit/test_session.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
import sys
23
from unittest.mock import patch, MagicMock, Mock, PropertyMock
34
import gc
45

@@ -14,6 +15,14 @@
1415
import databricks.sql
1516

1617

18+
def _forget_kernel_client_module():
19+
sys.modules.pop("databricks.sql.backend.kernel.client", None)
20+
import databricks.sql.backend.kernel as kernel_pkg
21+
22+
if hasattr(kernel_pkg, "client"):
23+
delattr(kernel_pkg, "client")
24+
25+
1726
class TestSession:
1827
"""
1928
Unit tests for Session functionality
@@ -427,7 +436,6 @@ class TestKernelRetryOptionsThreading:
427436
PACKAGE = "databricks.sql"
428437

429438
def test_retry_kwargs_threaded_into_kernel_client(self):
430-
import sys
431439
import types
432440

433441
pytest.importorskip(
@@ -442,6 +450,7 @@ def test_retry_kwargs_threaded_into_kernel_client(self):
442450
fake = types.ModuleType("databricks_sql_kernel")
443451
fake.KernelError = type("KernelError", (Exception,), {})
444452
fake.Session = MagicMock()
453+
_forget_kernel_client_module()
445454

446455
# Patch the kernel client class (imported lazily inside
447456
# _create_backend) and the provider builder; capture the kwargs
@@ -478,6 +487,55 @@ def test_retry_kwargs_threaded_into_kernel_client(self):
478487
conn.close()
479488

480489

490+
class TestKernelTelemetryOptionsThreading:
491+
"""The kernel path must forward telemetry options from connect()
492+
into ``KernelDatabricksClient`` so phase-7 PyO3 Session kwargs can
493+
be populated before the kernel opens its session."""
494+
495+
PACKAGE = "databricks.sql"
496+
497+
def test_telemetry_kwargs_threaded_into_kernel_client(self):
498+
import types
499+
500+
pytest.importorskip(
501+
"pyarrow",
502+
reason="kernel client module imports pyarrow at load",
503+
)
504+
505+
fake = types.ModuleType("databricks_sql_kernel")
506+
fake.KernelError = type("KernelError", (Exception,), {})
507+
fake.Session = MagicMock()
508+
_forget_kernel_client_module()
509+
510+
with patch.dict(sys.modules, {"databricks_sql_kernel": fake}), patch(
511+
"databricks.sql.backend.kernel.client.KernelDatabricksClient"
512+
) as mock_kernel_client, patch(
513+
"%s.session.get_python_sql_connector_auth_provider" % self.PACKAGE
514+
):
515+
instance = mock_kernel_client.return_value
516+
instance.open_session.return_value = SessionId(
517+
BackendType.SEA, "sess-id", None
518+
)
519+
520+
conn = databricks.sql.connect(
521+
server_hostname="foo",
522+
http_path="/sql/1.0/warehouses/abc",
523+
use_kernel=True,
524+
access_token="dapi-xyz",
525+
enable_telemetry=True,
526+
force_enable_telemetry=False,
527+
telemetry_batch_size=17,
528+
)
529+
try:
530+
_, kwargs = mock_kernel_client.call_args
531+
opts = kwargs["telemetry_options"]
532+
assert opts["enable_telemetry"] is True
533+
assert opts["force_enable_telemetry"] is False
534+
assert opts["telemetry_batch_size"] == 17
535+
finally:
536+
conn.close()
537+
538+
481539
class TestKernelUserAgentForwarding:
482540
"""user_agent_entry must reach the kernel on the use_kernel path —
483541
session.py folds it into the composed User-Agent and includes it in
@@ -488,7 +546,6 @@ class TestKernelUserAgentForwarding:
488546
PACKAGE = "databricks.sql"
489547

490548
def test_user_agent_entry_reaches_kernel_client_http_headers(self):
491-
import sys
492549
import types
493550

494551
pytest.importorskip(
@@ -498,6 +555,7 @@ def test_user_agent_entry_reaches_kernel_client_http_headers(self):
498555
fake = types.ModuleType("databricks_sql_kernel")
499556
fake.KernelError = type("KernelError", (Exception,), {})
500557
fake.Session = MagicMock()
558+
_forget_kernel_client_module()
501559

502560
with patch.dict(sys.modules, {"databricks_sql_kernel": fake}), patch(
503561
"databricks.sql.backend.kernel.client.KernelDatabricksClient"

tests/unit/test_telemetry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@ def test_token_federation_with_no_inner_provider(self):
270270
assert TelemetryHelper.get_auth_mechanism(fed) is None
271271
assert TelemetryHelper.get_auth_flow(fed) is None
272272

273+
def test_is_telemetry_enabled_returns_false_for_kernel(self):
274+
connection = MagicMock()
275+
connection.session.use_kernel = True
276+
connection.force_enable_telemetry = True
277+
connection.enable_telemetry = True
278+
279+
assert TelemetryHelper.is_telemetry_enabled(connection) is False
280+
273281

274282
class TestTelemetryFactory:
275283
"""Tests for TelemetryClientFactory lifecycle and management."""

0 commit comments

Comments
 (0)