Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ wal_format = "bincode"

[flush]
interval_secs = 10
wal_size_threshold_mb = 64
time_bucket_duration = "1h"
max_points_per_batch = 50000
# WAL group-commit: max entries to coalesce per write batch (0 = disabled)
wal_batch_size = 64
Expand All @@ -33,8 +31,15 @@ arrow_wal_enabled = true

[chdb]
session_data_path = "./chdb_data"
pool_size = 4 # same-path chDB connections for parallel flush inserts and queries
# Legacy: sets both pools when query_pool_size / write_pool_size are unset.
# pool_size = 4
query_pool_size = 4 # connections reserved for queries
write_pool_size = 4 # connections reserved for ingest/flush (isolated from queries)
schema_cache_max_entries = 10000
# tag_low_cardinality_max = 100000 # unset = use [cardinality].max_tag_values_per_measurement
insert_max_threads = 4
# insert_min_insert_block_size_rows = 100000 # match max_points_per_batch to reduce small parts
# insert_max_insert_block_size = 0 # 0 = ClickHouse engine default

[auth]
enabled = false
Expand Down
2 changes: 0 additions & 2 deletions deploy/examples/three-node.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ spec:
size: 10Gi
flush:
intervalSecs: 10
walSizeThresholdMb: 64
timeBucketDuration: "1h"
chdb:
sessionDataPath: /var/lib/hyperbytedb/chdb
retention:
Expand Down
2 changes: 0 additions & 2 deletions deploy/kind/manifests/hyperbytedb-cr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ spec:
poolSize: 4
flush:
intervalSecs: 5
walSizeThresholdMb: 256
timeBucketDuration: "1h"
maxPointsPerBatch: 100000
walBatchSize: 128
walBatchDelayUs: 0
Expand Down
2 changes: 1 addition & 1 deletion docs/deep-dive/deep-dive-read-path.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ On SELECT, `inject_tombstone_predicates()` loads all tombstones for the measurem

### Session model

chDB runs inside `spawn_blocking`. HyperbyteDB opens `chdb.pool_size` connections to the same `session_data_path`; each connection has its own `ChdbClient` mutex, so flush inserts and queries can overlap when the pool has more than one connection. Tune `server.max_concurrent_queries` (≥ `pool_size`) to cap in-flight query tasks.
chDB runs inside `spawn_blocking`. HyperbyteDB opens separate query and write connection pools to the same `session_data_path` (`chdb.query_pool_size` and `chdb.write_pool_size`, default 4 each). The read path uses the query pool only; each connection has its own `ChdbClient` mutex, so concurrent query tasks overlap when `query_pool_size > 1`. Ingest/flush uses the write pool, so heavy queries do not block inserts. Tune `server.max_concurrent_queries` (≥ `query_pool_size`) to cap in-flight query tasks.

### Output format

Expand Down
17 changes: 12 additions & 5 deletions docs/developer-guide/system-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,20 @@ HyperbyteDB uses **chDB** (embedded ClickHouse) as its query engine and storage

### Session management

Each chDB `Connection` is `Send` but not `Sync`. HyperbyteDB keeps a pool of connections to the same `session_data_path` (see `ChdbConnectionPool` in `adapters/chdb/connection_pool.rs`). Queries and inserts run in `spawn_blocking`, checking out one connection per task.
Each chDB `Connection` is `Send` but not `Sync`. HyperbyteDB opens **two** connection pools to the same `session_data_path` (see `ChdbConnectionPool` in `adapters/chdb/connection_pool.rs`):

### Single connection (`pool_size = 1`)
- **Query pool** (`chdb.query_pool_size`, default 4) — used by `ChdbQueryAdapter` for reads.
- **Write pool** (`chdb.write_pool_size`, default 4) — used by `ChdbNativeAdapter` for ingest/flush.

One connection: all chDB work serializes on that client's mutex (legacy / minimal footprint).
Legacy `chdb.pool_size` (when non-zero) sets both pools to the same size when the explicit keys are unset. Each pool is clamped to 1–128 connections.

### Connection pool (`pool_size > 1`)
Queries and inserts run in `spawn_blocking`, checking out one connection from the appropriate pool per task. Separate pools isolate flush inserts from concurrent queries.

### Single connection (`query_pool_size = 1` or `write_pool_size = 1`)

One connection in a pool: all work on that pool serializes on that client's mutex (minimal footprint).

### Connection pool (size > 1)

```rust
struct ChdbConnectionPool {
Expand All @@ -430,7 +437,7 @@ struct ChdbConnectionPool {
}
```

Round-robin checkout with `try_lock` on busy slots. Multiple connections share one process-global `EmbeddedServer` for the data path; each connection gets an independent `ChdbClient` mutex, so concurrent flush inserts and queries can overlap.
Round-robin checkout with `try_lock` on busy slots. Multiple connections share one process-global `EmbeddedServer` for the data path; each connection gets an independent `ChdbClient` mutex, so concurrent tasks within a pool can overlap.

### Output format

Expand Down
17 changes: 13 additions & 4 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ Controls the background WAL-to-chDB flush pipeline.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `interval_secs` | integer | `10` | How often the flush service runs (seconds) |
| `wal_size_threshold_mb` | integer | `64` | WAL size that triggers an immediate flush (MB) |
| `time_bucket_duration` | string | `"1h"` | Time bucket granularity used when grouping WAL entries for flush |
| `max_points_per_batch` | integer | `50000` | Max points per chDB insert batch (server clamps to 10k–500k; `0` uses the same default) |
| `wal_batch_size` | integer | `64` | WAL group-commit: max entries to coalesce per write batch; `0` = disabled |
| `wal_batch_delay_us` | integer | `200` | WAL group-commit: max microseconds to wait for more entries before flushing |
Expand All @@ -77,7 +75,14 @@ Embedded ClickHouse (chDB) query engine settings.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `session_data_path` | string | `"./chdb_data"` | chDB session state directory |
| `pool_size` | integer | `4` | Number of chDB connections to the same `session_data_path`. Each connection has its own client mutex, so flush inserts and concurrent queries overlap when `pool_size > 1`. Clamped to 1–32. For best overlap, set `server.max_concurrent_queries` ≥ `pool_size`. |
| `query_pool_size` | integer | `4` | chDB connections reserved for queries (`ChdbQueryAdapter`). Each connection has its own client mutex, so concurrent `spawn_blocking` query tasks overlap when > 1. Clamped to 1–128. For best overlap, set `server.max_concurrent_queries` ≥ `query_pool_size`. |
| `write_pool_size` | integer | `4` | chDB connections reserved for ingest/flush (`ChdbNativeAdapter`), isolated from the query pool so heavy queries do not block inserts. Clamped to 1–128. |
| `pool_size` | integer | `0` (unused) | **Legacy.** When non-zero and `query_pool_size` / `write_pool_size` are unset, applies the same size to both pools. Prefer explicit `query_pool_size` and `write_pool_size`. |
| `schema_cache_max_entries` | integer | `10000` | Max `(db, rp, measurement)` entries in the chDB native adapter schema and series caches. Oldest entries are evicted (LRU). |
| `insert_max_threads` | integer | `4` | ClickHouse `max_threads` for Arrow bulk inserts. Match CPU cores on the node. |
| `insert_min_insert_block_size_rows` | integer | `0` (unset) | ClickHouse `min_insert_block_size_rows` for Arrow bulk inserts. Set to ~`max_points_per_batch` to avoid many small parts. `0` = engine default. |
| `insert_max_insert_block_size` | integer | `0` (unset) | ClickHouse `max_insert_block_size` for Arrow bulk inserts (bytes). Caps part size for wide measurements. `0` = engine default. |
| `tag_low_cardinality_max` | integer | *(linked)* | Max distinct tag values per key before DDL uses plain `String` instead of `LowCardinality(String)`. When unset, uses `[cardinality].max_tag_values_per_measurement`. High-cardinality tags (trace IDs, request IDs) should stay as plain `String`. |

---

Expand Down Expand Up @@ -239,7 +244,8 @@ interval_secs = 10

[chdb]
session_data_path = "./chdb_data"
pool_size = 4
query_pool_size = 4
write_pool_size = 4

[logging]
level = "info"
Expand All @@ -266,6 +272,9 @@ interval_secs = 10

[chdb]
session_data_path = "/var/lib/hyperbytedb/chdb"
query_pool_size = 32
write_pool_size = 4
schema_cache_max_entries = 10000

[cluster]
enabled = true
Expand Down
4 changes: 0 additions & 4 deletions docs/user-guide/operator/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ spec:
storageClassName: fast-ssd
flush:
intervalSecs: 5
walSizeThresholdMb: 128
timeBucketDuration: "1h"
chdb:
sessionDataPath: /var/lib/hyperbytedb/chdb
auth:
Expand Down Expand Up @@ -226,8 +224,6 @@ HyperbyteDB stores WAL, metadata, Raft state, and chDB session data on the per-r
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `intervalSecs` | int32 | `10` | How often the WAL is flushed to chDB |
| `walSizeThresholdMb` | int32 | `64` | WAL size threshold that triggers an early flush |
| `timeBucketDuration` | string | `1h` | Parquet time-bucket width (`1h` or `1d`) |
| `maxPointsPerBatch` | int32 | `50000` | Max points per chDB insert batch (written to ConfigMap as `max_points_per_batch`) |
| `walBatchSize` | int32 | `64` | WAL group-commit batch size (`0` disables) |
| `walBatchDelayUs` | int64 | `200` | WAL group-commit delay in microseconds |
Expand Down
9 changes: 5 additions & 4 deletions docs/user-guide/resource-sizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ Size CPU and RAM from how many queries run at once and how heavy they are — no
max_concurrent_queries = 16

[chdb]
pool_size = 4
query_pool_size = 4
write_pool_size = 4
```
Set `max_concurrent_queries` ≥ `pool_size` so flush and queries can overlap. See [Configuration](configuration.md).
Set `max_concurrent_queries` ≥ `query_pool_size` so concurrent queries can use the query pool. Ingest/flush uses a separate `write_pool_size` pool. See [Configuration](configuration.md).
- If queries are slow but CPU is idle, you may be I/O-bound on disk — check storage type and free space before adding cores.
- If CPU is saturated while ingest stays healthy, reduce concurrency or simplify queries before scaling write throughput assumptions.

Expand Down Expand Up @@ -176,7 +177,7 @@ Example starting points (per node, before replication overhead):

| Symptom | Likely cause | What to try |
|---------|-------------|-------------|
| High CPU | Many concurrent or heavy queries | Lower `max_concurrent_queries`; narrow time ranges in queries; reduce `pool_size` if threads oversubscribe |
| High CPU | Many concurrent or heavy queries | Lower `max_concurrent_queries`; narrow time ranges in queries; reduce `query_pool_size` if threads oversubscribe |
| Slow queries | Wide scans, missing time filter | Add `WHERE time > ...`; reduce concurrent query load |
| High memory | chDB working set or Arrow cache | Cap concurrent queries; set `arrow_wal_enabled = false` if flush cache is the issue |
| Disk filling up | Retention too long or underestimated volume | Shorten retention policies; verify `[retention]` is enabled |
Expand Down Expand Up @@ -205,7 +206,7 @@ Start with defaults, deploy with realistic query patterns (same dashboards and a

## See Also

- [Configuration](configuration.md) — Tuning parameters (`max_concurrent_queries`, `pool_size`, flush settings)
- [Configuration](configuration.md) — Tuning parameters (`max_concurrent_queries`, `query_pool_size`, `write_pool_size`, flush settings)
- [Administration](administration.md) — Metrics and monitoring
- [Troubleshooting](troubleshooting.md) — Query timeouts, memory, and flush issues
- [V1 Stable Scope](v1-stable-scope.md) — Supported topologies and availability model
2 changes: 1 addition & 1 deletion docs/user-guide/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Data must be flushed from the WAL into chDB MergeTree tables before it becomes q

2. **Add a time range to your query.** Queries without `WHERE time > ...` scan all data.

3. **Cap concurrent queries.** Tune `server.max_concurrent_queries` so heavy queries do not oversubscribe the Tokio blocking pool. For overlapping chDB work, also set `chdb.pool_size` > 1 (same data path, multiple connections) and keep `max_concurrent_queries` ≥ `pool_size`.
3. **Cap concurrent queries.** Tune `server.max_concurrent_queries` so heavy queries do not oversubscribe the Tokio blocking pool. For overlapping chDB query work, set `chdb.query_pool_size` > 1 (same data path, multiple connections) and keep `max_concurrent_queries` ≥ `query_pool_size`. Ingest/flush uses a separate `write_pool_size` pool, so heavy queries do not block inserts.

4. **Narrow the time range** in your query to reduce scanned data volume.

Expand Down
7 changes: 5 additions & 2 deletions hyperbytedb/src/adapters/chdb/connection_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use parking_lot::Mutex;
use crate::error::HyperbytedbError;

pub const MIN_POOL_SIZE: usize = 1;
pub const MAX_POOL_SIZE: usize = 32;
pub const MAX_POOL_SIZE: usize = 128;
pub const DEFAULT_POOL_SIZE: usize = 4;
pub const DEFAULT_QUERY_POOL_SIZE: usize = 4;
pub const DEFAULT_WRITE_POOL_SIZE: usize = 4;

/// Clamp configured pool size to a safe range.
pub fn clamp_pool_size(size: usize) -> usize {
Expand Down Expand Up @@ -116,7 +118,8 @@ mod tests {
assert_eq!(clamp_pool_size(0), 1);
assert_eq!(clamp_pool_size(1), 1);
assert_eq!(clamp_pool_size(4), 4);
assert_eq!(clamp_pool_size(100), 32);
assert_eq!(clamp_pool_size(100), 100);
assert_eq!(clamp_pool_size(200), 128);
}

#[test]
Expand Down
Loading
Loading