faucet-source-databricks
Databricks SQL query source for the faucet-stream
ecosystem. Runs a SQL statement against a Databricks SQL Warehouse via the
Statement Execution API
(plain REST — no JDBC/ODBC driver, no Python) and streams the result rows as
typed JSON objects.
This is the query-results read path for Databricks — joins, aggregates,
filtered extracts. For full-table lakehouse scans and the write path, use
the Delta Lake connectors (faucet-source-delta
/ faucet-sink-delta). To load
into a Databricks SQL warehouse (Unity Catalog permissions, COPY INTO,
MERGE, exactly-once), use faucet-sink-databricks.
Highlights
- Async statement lifecycle — submit → poll until terminal → stream result
chunks (
EXTERNAL_LINKS+JSON_ARRAY: each chunk is downloaded from its presigned URL without the workspace token), following the chunk links, so results of any size stream —INLINEresults are capped at 25 MiB by Databricks. - Type-aware decode from the response
manifestcolumn schema (everyJSON_ARRAYcell is a string; decoded pertype_nameto typed JSON, withDECIMAL/largeLONGpreserved losslessly as strings). A result with two columns of the same name (a join'sid) fails with an error asking you to alias them, rather than one silently replacing the other. - Incremental replication — reads only rows not yet delivered, in cursor order, without skipping rows that share a cursor value; a crash replays at most one page (see Incremental replication).
- Bearer auth (PAT / OAuth M2M), inline or via the shared
auth:catalog. The auth and Statement Execution API client live infaucet-common-databricks(shared with the sink). This crate keeps its ownDatabricksAuth(same wire shape, converted withFrom) so its public API is unchanged. - Warm-up tolerant — a submit refused with
429/503(a serverless warehouse starting) is retried with backoff honouringRetry-After, polls retry transient5xx, and a401makes a shared provider mint a fresh token once. - Arrow-native fetch — behind the
arrowfeature,arrow_native: truefetches results asEXTERNAL_LINKS+ARROW_STREAMand decodes each chunk as an Arrow IPC stream, enabling the columnar fast path and skipping the per-cell JSON decode on the row path. See Arrow columnar (Parquet) mode.
Configuration
| Field | Type | Default | Notes |
|---|---|---|---|
workspace_url |
string | — (required) | https://<host>.cloud.databricks.com |
warehouse_id |
string | — (required) | target SQL Warehouse id |
sql |
string | — (required) | the query; supports :name params and a ${bookmark} token |
auth |
{ type, config } / { ref } |
— (required) | pat or token bearer, or a shared provider |
catalog / schema |
string? | — | default Unity Catalog catalog / schema |
parameters |
[{name, value, type?}] |
[] |
named :name SQL parameters |
wait_timeout_secs |
int | 50 |
server wait before async (0 or 5–50) |
poll_interval_secs |
int | 1 |
client poll cadence while running |
statement_timeout_secs |
int | 3600 |
client deadline for the statement to finish; on expiry it is cancelled and the read fails (0 = wait indefinitely) |
batch_size |
int | 1000 |
rows per emitted page; 0 is the "no batching" sentinel — the whole result set is emitted in a single page |
result_disposition |
external_links | inline |
external_links |
how the row path receives results. external_links downloads each chunk from a presigned cloud-storage URL (no size cap; the client must reach the workspace's storage); inline keeps results in the API response, which Databricks fails above 25 MiB. Ignored under arrow_native. |
arrow_native |
bool | false |
fetch as EXTERNAL_LINKS + ARROW_STREAM and decode Arrow IPC; enables the columnar fast path. Requires the arrow feature and replication: full. See Arrow columnar (Parquet) mode. |
replication |
{ type: full | incremental, column, initial_value } |
full |
incremental cursor |
state_key |
string? | derived | explicit bookmark key. The query fingerprint changed in #839 (a fixed FNV-1a hash replaced Rust's DefaultHasher, which is not stable across releases), so a library pipeline relying on the derived key starts once from its initial position after upgrading; set state_key to pin it. Runs through the faucet CLI are unaffected — the CLI keys state by pipeline and row. |
pipeline:
source:
type: databricks
config:
workspace_url: https://dbc-xxxx.cloud.databricks.com
warehouse_id: 0123456789abcdef
sql: SELECT id, ts FROM events
auth:
replication:
Incremental replication
replication: { type: incremental, column, initial_value } wraps the SQL as
SELECT * FROM (<sql>) AS q WHERE q.`<column>` >= :_faucet_bookmark ORDER BY q.`<column>` ,
so the warehouse filters and orders by the cursor; initial_value is the
inclusive lower bound for the first run. To apply the cursor further inside
the SQL, write ${bookmark} there with >= (it binds the same named
parameter); a > ${bookmark} is refused at load time because it would skip
rows that share the bookmark value.
Rows that share a cursor value are never skipped. The bookmark stores the
last cursor value and a 128-bit fingerprint of every row already emitted at
it — {"value": "...", "boundary": ["<fingerprint>", ...]}. The next read
re-reads that value with >= and drops only the rows it already emitted, so a
row committed later with the last updated_at is delivered. Above 10,000 rows
at one value the set is dropped and the value is re-read in full: duplicates,
never loss. Every page carries the bookmark after its last row, so a crash
replays at most one page. A bare bookmark stored by an earlier release reads
as that value with an empty boundary (its rows are re-delivered once). Rows
whose cursor is NULL are never read.
Arrow columnar (Parquet) mode
Behind the crate-local arrow Cargo feature, setting arrow_native: true
submits the statement with EXTERNAL_LINKS disposition and ARROW_STREAM
format; each result chunk's presigned link is fetched and decoded as an Arrow
IPC stream rather than a JSON_ARRAY. This has two effects:
- the opt-in columnar fast path (RFC 0002 / #375) — when the sink is also
Arrow-native (the Parquet or
Delta Lake sink) and no
Value-shaped transform is configured, records move end-to-end as ArrowRecordBatches with noserde_json::Valuematerialization; and - a faster row path — even into a JSON sink, decoding Arrow batches skips
the per-cell string→JSON decode the default
JSON_ARRAYpath pays.
arrow_native: true requires the arrow feature and only works with
replication: full — config validation rejects arrow_native combined with
incremental replication, because the columnar path does not track the
per-row cursor bookmark. The default (arrow_native: false) keeps the
JSON_ARRAY row path.
# databricks(arrow) → parquet — runs Arrow end-to-end
pipeline:
source:
type: databricks
config:
workspace_url: https://dbc-xxxx.cloud.databricks.com
warehouse_id: 0123456789abcdef
sql: SELECT id, ts, payload FROM events
auth:
arrow_native: true # requires the `arrow` feature; replication must be `full`
sink:
type: file
config:
path: ./out/
format: parquet
Enable it with cargo add faucet-source-databricks --features arrow (library)
or cargo install faucet-cli --features "source-databricks,arrow" (CLI).
Out of scope (v1)
discover() via INFORMATION_SCHEMA and reading from Unity Catalog Volumes.
To write to a SQL warehouse, use the
faucet-sink-databricks sink.
License: MIT OR Apache-2.0.
Capabilities
Declares columnar (Arrow) output only when the crate is built with the arrow feature and the config sets arrow_native: true; otherwise it declares nothing. faucet list --json prints these declared capabilities.
Timeouts, cancellation and retries
Every request — statement submit and polls, result chunks, presigned
external links — runs through the pipeline's CallContext (#880): the run's
cancel token stops an in-flight request or poll (an abandoned statement is
cancelled on the warehouse), any caller deadline ends it, and each is metered as
a round trip (submit, poll, chunk). The HTTP client's connect / idle-read
timeouts, statement_timeout_secs and the 429 / 503 retry are unchanged.