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
2 changes: 1 addition & 1 deletion config.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

kafka:
brokers:
- localhost:9092
- localhost:19092
group: dfe-loader-dev
topics:
- events
Expand Down
1 change: 0 additions & 1 deletion docker-compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ services:
CLICKHOUSE_PASSWORD: ""
volumes:
- clickhouse-data:/var/lib/clickhouse
- ./tests/fixtures/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD", "clickhouse-client", "--query", "SELECT 1"]
interval: 5s
Expand Down
54 changes: 27 additions & 27 deletions docs/config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@
"table": "dlq"
}
},
"memory": {
"description": "Memory limits. **Restart required.**",
"$ref": "#/$defs/MemoryConfig",
"default": {
"limit_bytes": 0,
"pressure_threshold": 0.8
}
},
"routing": {
"description": "Table and tenant routing. **Hot-reloaded**, `routing.dlq` aside.",
"$ref": "#/$defs/RoutingConfig",
Expand Down Expand Up @@ -233,14 +241,6 @@
"flush_age_secs": 5
}
},
"memory": {
"description": "Memory limits. **Hot-reloaded.**",
"$ref": "#/$defs/MemoryConfig",
"default": {
"limit_bytes": 0,
"pressure_threshold": 0.8
}
},
"timestamp_dq": {
"description": "Timestamp data quality validation. **Hot-reloaded.**",
"$ref": "#/$defs/TimestampDqConfig",
Expand Down Expand Up @@ -813,7 +813,7 @@
}
},
"InsertFormat": {
"description": "Insert format for data writes.\n\nControls how rows are encoded and sent to `ClickHouse`. `RowBinary` is the\ndefault — it uses schema reflection to encode `Map<String, Value>` directly\nto binary, so `ClickHouse` skips JSON parsing entirely. This significantly\nreduces CPU load on the `ClickHouse` cluster at scale.\n\n`JsonEachRow` is the fallback — simpler, self-describing, but the `ClickHouse`\nserver pays the cost of parsing every JSON row on ingest.",
"description": "Insert format for data writes.\n\nControls how rows are encoded and sent to `ClickHouse`. `RowBinary` is the\ndefault — it uses schema reflection to encode `Map<String, Value>` directly\nto binary, so `ClickHouse` parses only `JSON` columns, which travel as text.\n\n`JsonEachRow` is the fallback — simpler, self-describing, but the `ClickHouse`\nserver pays the cost of parsing every JSON row on ingest.",
"oneOf": [
{
"description": "Schema-reflected `RowBinary` (default).\n\nFetches schema from `system.columns`, encodes values to binary client-side.\n`ClickHouse` receives pre-columnarised data — zero server-side parsing.\nTotal CPU (client + cluster) is significantly lower than `JSONEachRow`.",
Expand Down Expand Up @@ -1367,6 +1367,24 @@
}
}
},
"MemoryConfig": {
"type": "object",
"properties": {
"limit_bytes": {
"description": "Maximum memory for buffers in bytes. 0 = auto-detect (67% of available)",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"pressure_threshold": {
"description": "Pressure threshold (0.0-1.0) - pause consumption above this",
"type": "number",
"format": "double",
"default": 0.8
}
}
},
"RoutingConfig": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1585,24 +1603,6 @@
}
}
},
"MemoryConfig": {
"type": "object",
"properties": {
"limit_bytes": {
"description": "Maximum memory for buffers in bytes. 0 = auto-detect (67% of available)",
"type": "integer",
"format": "uint",
"minimum": 0,
"default": 0
},
"pressure_threshold": {
"description": "Pressure threshold (0.0-1.0) - pause consumption above this",
"type": "number",
"format": "double",
"default": 0.8
}
}
},
"TimestampDqConfig": {
"type": "object",
"properties": {
Expand Down
43 changes: 21 additions & 22 deletions docs/config-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ properties:
- dfe_fetcher_dlq
- dfe_transform_dlq
table: dlq
memory:
description: Memory limits. **Restart required.**
$ref: "#/$defs/MemoryConfig"
default:
limit_bytes: 0
pressure_threshold: 0.8
routing:
description: Table and tenant routing. **Hot-reloaded**, `routing.dlq` aside.
$ref: "#/$defs/RoutingConfig"
Expand Down Expand Up @@ -221,12 +227,6 @@ properties:
flush_bytes: 67108864
flush_rows: 20000
flush_age_secs: 5
memory:
description: Memory limits. **Hot-reloaded.**
$ref: "#/$defs/MemoryConfig"
default:
limit_bytes: 0
pressure_threshold: 0.8
timestamp_dq:
description: Timestamp data quality validation. **Hot-reloaded.**
$ref: "#/$defs/TimestampDqConfig"
Expand Down Expand Up @@ -727,8 +727,7 @@ $defs:

Controls how rows are encoded and sent to `ClickHouse`. `RowBinary` is the
default — it uses schema reflection to encode `Map<String, Value>` directly
to binary, so `ClickHouse` skips JSON parsing entirely. This significantly
reduces CPU load on the `ClickHouse` cluster at scale.
to binary, so `ClickHouse` parses only `JSON` columns, which travel as text.

`JsonEachRow` is the fallback — simpler, self-describing, but the `ClickHouse`
server pays the cost of parsing every JSON row on ingest.
Expand Down Expand Up @@ -1238,6 +1237,20 @@ $defs:
picks for its tenant.
type: string
default: dlq
MemoryConfig:
type: object
properties:
limit_bytes:
description: Maximum memory for buffers in bytes. 0 = auto-detect (67% of available)
type: integer
format: uint
minimum: 0
default: 0
pressure_threshold:
description: Pressure threshold (0.0-1.0) - pause consumption above this
type: number
format: double
default: 0.8
RoutingConfig:
type: object
properties:
Expand Down Expand Up @@ -1446,20 +1459,6 @@ $defs:
format: uint64
minimum: 0
default: 5
MemoryConfig:
type: object
properties:
limit_bytes:
description: Maximum memory for buffers in bytes. 0 = auto-detect (67% of available)
type: integer
format: uint
minimum: 0
default: 0
pressure_threshold:
description: Pressure threshold (0.0-1.0) - pause consumption above this
type: number
format: double
default: 0.8
TimestampDqConfig:
type: object
properties:
Expand Down
7 changes: 0 additions & 7 deletions reference/common_header.csv

This file was deleted.

78 changes: 0 additions & 78 deletions reference/common_header.sql

This file was deleted.

7 changes: 3 additions & 4 deletions src/clickhouse/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ impl std::fmt::Display for Transport {
///
/// Controls how rows are encoded and sent to `ClickHouse`. `RowBinary` is the
/// default — it uses schema reflection to encode `Map<String, Value>` directly
/// to binary, so `ClickHouse` skips JSON parsing entirely. This significantly
/// reduces CPU load on the `ClickHouse` cluster at scale.
/// to binary, so `ClickHouse` parses only `JSON` columns, which travel as text.
///
/// `JsonEachRow` is the fallback — simpler, self-describing, but the `ClickHouse`
/// server pays the cost of parsing every JSON row on ingest.
Expand Down Expand Up @@ -135,7 +134,7 @@ pub struct ClickHouseConfig {
/// Insert format — how rows are encoded for INSERT.
///
/// `RowBinary` (default): schema-reflected binary encoding. `ClickHouse`
/// skips JSON parsing. Lower total CPU across client + cluster.
/// parses only `JSON` columns, which travel as text.
///
/// `JsonEachRow`: JSON text via HTTP. Self-describing, `ClickHouse` coerces
/// types server-side. Higher cluster CPU but simpler.
Expand Down Expand Up @@ -352,7 +351,7 @@ impl ClickHouseConfig {
// JSONEachRow is HTTP-only: the dynamic insert path sends JSONEachRow
// through `Client::insert_formatted_with` (HTTP). A native-transport
// client has no HTTP endpoint, so this combination fails at insert
// time. Reject it at config time so `config-check` catches it.
// time. Rejected here, when the pipeline starts and before readiness.
if self.transport == Transport::Native && self.insert_format == InsertFormat::JsonEachRow {
return Err(
"insert_format 'json_each_row' requires protocol 'http' (JSONEachRow is \
Expand Down
2 changes: 1 addition & 1 deletion src/clickhouse/inserter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ impl Inserter {
/// Set the insert format.
///
/// - `RowBinary` (default): schema-reflected binary via `DynamicInsert`.
/// `ClickHouse` skips JSON parsing — lower cluster CPU at scale.
/// `ClickHouse` parses only `JSON` columns, which travel as text.
/// - `JsonEachRow`: NDJSON via `InsertFormatted`. Self-describing fallback.
#[must_use]
pub fn with_insert_format(mut self, format: InsertFormat) -> Self {
Expand Down
4 changes: 2 additions & 2 deletions src/config/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ pub struct Config {
pub batch_processing: BatchProcessingConfig,
/// Kafka DLQ topics loaded into a table of their own. **Restart required.**
pub dlq_load: DlqLoadConfig,
/// Memory limits. **Restart required.**
pub memory: MemoryConfig,

// --- Hot-reloaded (takes effect on next batch) ---
/// Table and tenant routing. **Hot-reloaded**, `routing.dlq` aside.
pub routing: RoutingConfig,
/// Buffer flush thresholds. **Hot-reloaded.**
pub buffer: BufferConfig,
/// Memory limits. **Hot-reloaded.**
pub memory: MemoryConfig,
/// Timestamp data quality validation. **Hot-reloaded.**
pub timestamp_dq: TimestampDqConfig,
/// Field name sanitisation. **Hot-reloaded.**
Expand Down
9 changes: 4 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ impl ServiceApp for App {
}

fn load_config(&self, path: Option<&str>) -> Result<Self::Config, CliError> {
// run_app has set the cascade up from --config, which also reads
// ./.env into the environment Config::load reads.
// run_app has set the cascade up from --config. Without --config it
// also reads ./.env into the environment Config::load reads.
let config = Config::load(path).map_err(|e| CliError::Config(e.to_string()))?;
config
.validate()
Expand Down Expand Up @@ -128,9 +128,8 @@ impl ServiceApp for App {
.clone()
.unwrap_or_else(|| Arc::new(config.scaling.build_pressure()));

// The engine KEDA reads takes its gates from scalo's cascade, which
// reads neither the `--config` file nor the `DFE_LOADER__` env form,
// so a gate set either way is reported here and ignored there (#160).
// The engine KEDA reads takes its gates from scalo's cascade, so a
// gate it did not pick up is reported here.
if scaling.is_enabled() != config.scaling.enabled {
warn!(
configured = config.scaling.enabled,
Expand Down
Loading
Loading