Commit 8d80062
Fall back to remote runtime on Spark Connect when the legacy namespace is unavailable (#1469)
## Summary
Importing `databricks.sdk.runtime` on a Spark Connect runtime (e.g.
shared-access-mode clusters) no longer raises
`CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT` at import time. When the legacy
user namespace cannot be materialized, the import now logs a warning and
falls back to the existing Spark Connect-compatible remote
implementation, so `WorkspaceClient()` construction succeeds on such
clusters.
Fixes #1463. Carries forward @sd-db's work from #1464 (closed because
fork PRs in this repo cannot run tests).
## Why
`WorkspaceClient.__init__` eagerly builds `dbutils` via `_make_dbutils`,
which on a cluster does `from databricks.sdk.runtime import dbutils`.
That import calls
`UserNamespaceInitializer.getOrCreate().get_namespace_globals()`,
materializing a legacy `SparkContext`. On a Spark Connect cluster this
raises `CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT` — a
`pyspark.errors.PySparkRuntimeError`, not an `ImportError` — so the
existing `except ImportError:` does not catch it and the error escapes
the import, crashing `WorkspaceClient` construction before any API call.
This is what databricks/dbt-databricks#1252 hits in Python models on
shared clusters.
The existing `except ImportError` branch is already the Spark
Connect-compatible path (it builds `spark` via `DatabricksSession` and
`dbutils` via `RemoteDbUtils`), so this PR routes the materialization
failure there.
A complementary follow-up — making `WorkspaceClient.dbutils` lazy via a
`cached_property` so consumers that never touch it skip the build
entirely — is noted in #1463 as a separate discussion since it touches
generated code. Related issue #986 (off-cluster eager `RemoteDbUtils`
auth failure) is the symmetric case and is intentionally not addressed
here; the lazy-dbutils follow-up would unify both.
## What changed
### Behavioral changes
On a Spark Connect runtime, importing `databricks.sdk.runtime` now logs
a `WARNING` and uses the remote implementation instead of raising at
import time. When `dbruntime` is absent (off-cluster) or the namespace
materializes successfully (classic runtime), behavior is unchanged.
### Internal changes
`databricks/sdk/runtime/__init__.py`: the runtime-namespace block is
restructured into a single `try` with sibling `except ImportError`
(existing — "not in a classic runtime") and `except Exception` (new —
Spark Connect / `CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT`, logged)
clauses, plus an `if not _use_runtime_namespace:` guard over the
existing — unchanged — OSS/remote block. The catch is intentionally
broad rather than typed on `PySparkRuntimeError` to avoid pulling
`pyspark` in at SDK import time just to narrow the exception type; the
inline comment notes this.
## How is this tested?
New `tests/test_runtime.py` simulates a Spark Connect runtime by
injecting a fake `dbruntime` whose `get_namespace_globals()` raises
`CONTEXT_UNAVAILABLE_FOR_REMOTE_CLIENT`, and asserts that:
- reloading `databricks.sdk.runtime` survives the failure and falls back
(`is_local_implementation is True`, `dbutils is not None`)
- `WorkspaceClient(config=…)` constructs without raising — the direct
reproduction of the reported failure
Verified red→green locally. Full unit test suite (2098 tests) passes
with no regressions.
---------
Signed-off-by: Shubham Dhal <shubham.dhal@databricks.com>
Signed-off-by: Divyansh Vijayvergia <171924202+Divyansh-db@users.noreply.github.com>
Co-authored-by: Shubham Dhal <shubham.dhal@databricks.com>1 parent d01f89a commit 8d80062
3 files changed
Lines changed: 82 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
96 | 99 | | |
97 | | - | |
98 | | - | |
99 | 100 | | |
100 | 101 | | |
101 | 102 | | |
| |||
105 | 106 | | |
106 | 107 | | |
107 | 108 | | |
| 109 | + | |
108 | 110 | | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
109 | 126 | | |
110 | 127 | | |
111 | 128 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
0 commit comments