Skip to content

Commit 76dbbf0

Browse files
committed
Add Scala and Java examples for TableProvider API settings
Add tabbed code examples showing how to use spark.clickhouse.read.settings and spark.clickhouse.write.* options in Python, Scala, and Java.
1 parent c9550ae commit 76dbbf0

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

docs/integrations/data-ingestion/apache-spark/spark-native-connector.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,9 @@ Use `spark.clickhouse.read.settings` to apply ClickHouse server settings to read
17101710

17111711
To scope the settings to a single read, set the config, force the action, then unset:
17121712

1713+
<Tabs groupId="language">
1714+
<TabItem value="python" label="Python" default>
1715+
17131716
```python
17141717
spark.conf.set("spark.clickhouse.read.settings", "max_execution_time=300,max_memory_usage=10000000000")
17151718

@@ -1725,8 +1728,50 @@ result = spark.read \
17251728
spark.conf.unset("spark.clickhouse.read.settings")
17261729
```
17271730

1731+
</TabItem>
1732+
<TabItem value="scala" label="Scala">
1733+
1734+
```scala
1735+
spark.conf.set("spark.clickhouse.read.settings", "max_execution_time=300,max_memory_usage=10000000000")
1736+
1737+
// Force the action before unsetting — Spark reads are lazy
1738+
val result = spark.read
1739+
.format("clickhouse")
1740+
.option("host", "your-host")
1741+
.option("database", "default")
1742+
.option("table", "my_table")
1743+
.load()
1744+
.collect()
1745+
1746+
spark.conf.unset("spark.clickhouse.read.settings")
1747+
```
1748+
1749+
</TabItem>
1750+
<TabItem value="java" label="Java">
1751+
1752+
```java
1753+
spark.conf().set("spark.clickhouse.read.settings", "max_execution_time=300,max_memory_usage=10000000000");
1754+
1755+
// Force the action before unsetting — Spark reads are lazy
1756+
Row[] result = spark.read()
1757+
.format("clickhouse")
1758+
.option("host", "your-host")
1759+
.option("database", "default")
1760+
.option("table", "my_table")
1761+
.load()
1762+
.collect();
1763+
1764+
spark.conf().unset("spark.clickhouse.read.settings");
1765+
```
1766+
1767+
</TabItem>
1768+
</Tabs>
1769+
17281770
For write-side settings (e.g. per-write batch size), pass `spark.clickhouse.write.*` options directly on the writer:
17291771

1772+
<Tabs groupId="language">
1773+
<TabItem value="python" label="Python" default>
1774+
17301775
```python
17311776
df.write \
17321777
.format("clickhouse") \
@@ -1739,6 +1784,39 @@ df.write \
17391784
.save()
17401785
```
17411786

1787+
</TabItem>
1788+
<TabItem value="scala" label="Scala">
1789+
1790+
```scala
1791+
df.write
1792+
.format("clickhouse")
1793+
.option("host", "your-host")
1794+
.option("database", "default")
1795+
.option("table", "my_table")
1796+
.option("spark.clickhouse.write.batchSize", "50000")
1797+
.option("spark.clickhouse.write.format", "arrow")
1798+
.mode("append")
1799+
.save()
1800+
```
1801+
1802+
</TabItem>
1803+
<TabItem value="java" label="Java">
1804+
1805+
```java
1806+
df.write()
1807+
.format("clickhouse")
1808+
.option("host", "your-host")
1809+
.option("database", "default")
1810+
.option("table", "my_table")
1811+
.option("spark.clickhouse.write.batchSize", "50000")
1812+
.option("spark.clickhouse.write.format", "arrow")
1813+
.mode("append")
1814+
.save();
1815+
```
1816+
1817+
</TabItem>
1818+
</Tabs>
1819+
17421820
### Common settings {#query-settings-common}
17431821

17441822
| Option key | Example value | Purpose |
@@ -1754,6 +1832,65 @@ df.write \
17541832
To apply ClickHouse server settings only to read requests (not writes), use `spark.clickhouse.read.settings` set via `spark.conf.set(...)` instead of catalog-level `option.clickhouse_setting_*` entries. Catalog properties apply to all operations for the lifetime of the catalog and cannot be scoped to reads only.
17551833
:::
17561834

1835+
## Timeout configuration {#timeout-configuration}
1836+
1837+
Timeouts in the Spark connector operate at three independent layers. Misdiagnosing which layer is responsible for a timeout is the most common source of confusion.
1838+
1839+
### Connector-internal timeouts {#timeout-connector}
1840+
1841+
The connector enforces its own timeouts that are independent of any Spark or ClickHouse setting:
1842+
1843+
| Behavior | Value | Configurable |
1844+
|---|---|---|
1845+
| Query timeout | **60 seconds** | No — hard-coded in the connector |
1846+
| Insert timeout | **None** | No — inserts run until the network drops the connection |
1847+
1848+
The 60-second query cap is enforced by the connector regardless of `max_execution_time` or any other server setting. If a read query takes longer than 60 seconds end-to-end, the connector will abort it. There is no `spark.clickhouse.*` setting to override this value.
1849+
1850+
Inserts have no connector-level timeout. This means a stalled or very slow insert will hang until a network device terminates the connection — which can produce a **"Broken pipe"** error (see [Connection resets on AWS PrivateLink or NLB](#troubleshooting-connection-resets) below).
1851+
1852+
### Java client connection timeouts {#timeout-java-client}
1853+
1854+
These are passed through to the underlying ClickHouse Java client using the `option.<key>` prefix on catalog properties. They control the TCP/HTTP connection lifecycle.
1855+
1856+
| Catalog property | Default | Unit | What it controls |
1857+
|---|---|---|---|
1858+
| `option.socket_timeout` | `0` (unlimited) | ms | Deadline for each TCP read/write operation. `0` means no deadline — the call blocks indefinitely if the server stops responding. |
1859+
| `option.connection_timeout` | not set | ms | Time allowed to establish a new TCP connection to ClickHouse. |
1860+
| `option.connection_ttl` | `-1` (unlimited) | ms | Maximum lifetime of a pooled connection. After this time, the connection is retired and not reused. **Set this below the idle timeout of any network appliance between Spark and ClickHouse** (e.g., `300000` for AWS NLB). |
1861+
| `option.http_keep_alive_timeout` | server default | ms | Overrides the HTTP keep-alive timeout. Set to `0` to disable keep-alive on network paths with aggressive idle-connection cutoffs. |
1862+
1863+
### ClickHouse server-side timeouts {#timeout-server}
1864+
1865+
These are ClickHouse query settings sent with each request. They instruct the ClickHouse server to enforce a limit, independent of what the connector or TCP layer does.
1866+
1867+
| Catalog property | Default | Unit | What it controls |
1868+
|---|---|---|---|
1869+
| `option.clickhouse_setting_max_execution_time` | `0` (unlimited) | seconds | Server-side hard cap on query execution time. Useful for preventing runaway reads from consuming server resources, but **does not override the connector's 60-second query timeout**. |
1870+
| `option.clickhouse_setting_session_timeout` | `60` | seconds | HTTP session lifetime on the server. |
1871+
1872+
### Common timeout scenarios {#timeout-scenarios}
1873+
1874+
**Long-running reads (>60 seconds)**
1875+
1876+
The connector's hard-coded 60-second query timeout will abort reads that take longer, even if the server could complete them. There is no configuration knob to extend or disable this cap. The only workaround is to reduce the amount of data returned by a single query so each read completes within 60 seconds:
1877+
1878+
- Add `WHERE` predicates to filter rows before they reach Spark.
1879+
- Use partition pruning so the connector issues one query per partition rather than one large scan.
1880+
1881+
**Insert "Broken pipe" or "Connection reset" errors**
1882+
1883+
Typically caused by a network appliance (AWS NLB, PrivateLink, corporate proxy) silently dropping idle connections. The connector's connection pool holds connections open between operations; if the appliance's idle timeout (350 seconds for AWS NLB) expires, the connection is dropped at the network level without notification. The connector then tries to reuse a dead connection and gets a broken pipe.
1884+
1885+
```text
1886+
spark.sql.catalog.clickhouse.option.connection_ttl 300000
1887+
spark.sql.catalog.clickhouse.option.socket_timeout 300000
1888+
spark.sql.catalog.clickhouse.option.http_keep_alive_timeout 0
1889+
```
1890+
1891+
Setting `connection_ttl` below the appliance's idle timeout forces the connector to retire connections proactively. See [Connection resets on AWS PrivateLink or NLB](#troubleshooting-connection-resets) for full details.
1892+
1893+
17571894
## Performance tuning {#performance-tuning}
17581895

17591896
### Read performance {#read-performance}
@@ -1831,6 +1968,31 @@ spark.catalog.refreshTable("clickhouse.database.table_name")
18311968

18321969
---
18331970

1971+
### Connection resets on AWS PrivateLink or NLB {#troubleshooting-connection-resets}
1972+
1973+
**Symptom**: Reads or writes intermittently fail with `Broken pipe`, `Connection reset by peer`, or `java.io.IOException: Connection closed` — particularly on longer-running jobs or after a period of inactivity.
1974+
1975+
**Cause**: AWS Network Load Balancers (NLB) and PrivateLink silently drop TCP connections that have been idle for **350 seconds**. The connector's connection pool keeps HTTP connections alive between operations. When the NLB drops a pooled connection, neither the connector nor ClickHouse is notified. The next operation that picks up that connection fails immediately.
1976+
1977+
Inserts are especially vulnerable: there is no connector-level insert timeout, so a slow or large insert can stall mid-flight when the underlying connection is cut.
1978+
1979+
**Fix**: Retire pooled connections before the NLB does by setting `connection_ttl` below 350 seconds. Also set `socket_timeout` to bound individual operations and disable HTTP keep-alive to prevent stale connections from persisting in the pool:
1980+
1981+
```text
1982+
spark.sql.catalog.clickhouse.option.connection_ttl 300000
1983+
spark.sql.catalog.clickhouse.option.socket_timeout 300000
1984+
spark.sql.catalog.clickhouse.option.http_keep_alive_timeout 0
1985+
```
1986+
1987+
For very large inserts that are genuinely expected to take several minutes, switch to async inserts so the server acknowledges receipt quickly and the connector is not waiting on a long-lived open connection:
1988+
1989+
```text
1990+
spark.sql.catalog.clickhouse.option.clickhouse_setting_async_insert 1
1991+
spark.sql.catalog.clickhouse.option.clickhouse_setting_wait_for_async_insert 1
1992+
```
1993+
1994+
---
1995+
18341996
### KEEPER_EXCEPTION during writes {#troubleshooting-keeper-exception}
18351997

18361998
**Symptom**: Writes fail with `Coordination::Exception: Can't get data for node ... node doesn't exist (KEEPER_EXCEPTION)`. Retries may produce duplicate data.

0 commit comments

Comments
 (0)