ClickHouse CDC materialization¶
ClickHouse CDC materialization turns the append-only dpone_cdc_* log written
by ClickHouseCdcSinkApplier into a current-state serving table. It is the
serving layer after dpone ops cdc-runtime-run --mode live: the runtime remains
offset-critical and append-only, while this command can rebuild the latest view
of each source key on demand.
The first production route is mssql -> clickhouse, but the table contract is
generic for any future CDC source that writes the normalized dpone_cdc_*
columns.
For business-facing current-state tables with declared ClickHouse types, use ClickHouse CDC typed materialization after the append-only CDC log is populated.
Workflow¶
- Run the CDC runtime until the ClickHouse CDC log table contains durable events.
- Choose a serving table name separate from the append-only log.
- Run
dpone ops cdc-materialize-clickhouse. - Review
cdc_materialization.json,cdc_materialization.md, and row counts. - Promote dashboards and downstream jobs to the serving table only after the
materialization report has
passed=true.
Example:
dpone ops cdc-materialize-clickhouse \
--cdc-dataset analytics.orders_cdc \
--target-dataset serving.orders_current \
--unique-key order_id \
--sink-connection-id clickhouse-prod \
--credentials-source env \
--delete-mode exclude_deleted \
--output-dir test_artifacts/cdc_materialization/mssql_to_clickhouse/orders \
--format json
For compound keys, repeat --unique-key:
dpone ops cdc-materialize-clickhouse \
--cdc-dataset analytics.order_lines_cdc \
--target-dataset serving.order_lines_current \
--unique-key order_id \
--unique-key line_id \
--sink-connection-id clickhouse-prod
Delete modes¶
| Mode | Behavior | Use when |
|---|---|---|
--delete-mode exclude_deleted |
Latest deleted keys are omitted from the serving table. | Consumers expect only active current rows. |
--delete-mode tombstone |
Latest deleted keys remain in the serving table with dpone_cdc_deleted = 1. |
Consumers need deletion audit, cache invalidation, or soft-delete semantics. |
Both modes dedupe by dpone_cdc_unique_key_hash and choose the latest event by
CDC ingest time, source position length/value, source sequence, and event hash.
The command writes through a shadow-table replace so failed materializations do
not partially mutate the target table.
Serving table columns¶
The serving table keeps the normalized CDC metadata and canonical JSON payload:
| Column | Purpose |
|---|---|
dpone_cdc_unique_key_hash |
Stable hash used as the materialization dedupe key. |
dpone_cdc_unique_key_json |
Canonical JSON key payload for audits and debugging. |
dpone_cdc_payload_json |
Latest source row payload as canonical JSON. |
dpone_cdc_operation |
Latest operation for the key. |
dpone_cdc_position |
Source LSN, Change Tracking version, or future source position token. |
dpone_cdc_deleted |
1 when the latest event is a delete. |
dpone_cdc_materialized_at |
Time when the serving row was rebuilt. |
Evidence artifacts¶
| Artifact | Purpose |
|---|---|
cdc_materialization.json |
Stable machine-readable contract for CI, release gates, and run registry ingestion. |
cdc_materialization.md |
Human-readable review artifact for operators. |
| ClickHouse serving table | Current-state table rebuilt from the CDC log. |
Local Docker integration test¶
The opt-in vendor-live test proves the full mssql -> clickhouse path:
- SQL Server Change Tracking produces insert, update, and delete events.
CdcRuntimeOrchestratorwrites the append-only ClickHouse CDC log.ClickHouseCdcMaterializationServicebuilds active-row and tombstone serving tables.- The test checks current-state row counts and materialization evidence.
Run it locally:
docker compose -f docker/docker-compose.integration.yml up -d mssql clickhouse
DPONE_RUN_INTEGRATION=1 \
DPONE_IT_MSSQL_HOST=127.0.0.1 \
DPONE_IT_MSSQL_PORT=51433 \
DPONE_IT_MSSQL_DATABASE=dpone_it \
DPONE_IT_MSSQL_CDC_HOST=127.0.0.1 \
DPONE_IT_MSSQL_CDC_PORT=51433 \
DPONE_IT_MSSQL_CDC_DATABASE=dpone_it \
DPONE_IT_MSSQL_PASSWORD='Dp0ne.Strong.Pw.2026!' \
DPONE_IT_MSSQL_CDC_PASSWORD='Dp0ne.Strong.Pw.2026!' \
DPONE_IT_MSSQL_TRUST_SERVER_CERTIFICATE=yes \
DPONE_IT_CH_HOST=127.0.0.1 \
DPONE_IT_CH_PORT=59000 \
DPONE_IT_CH_HTTP_PORT=58123 \
DPONE_IT_CH_DATABASE=dpone_it \
DPONE_IT_CH_USER=default \
DPONE_IT_CH_PASSWORD=dpone \
uv run pytest tests/integration/mssql/test_mssql_clickhouse_live_cdc_runtime_integration.py -q
Runbook¶
| Symptom | Action |
|---|---|
clickhouse_cdc_materialization.failed |
Inspect metrics.error, ClickHouse permissions, source CDC table existence, disk space, and target database permissions. |
| Serving table has fewer rows than expected | Check --delete-mode; exclude_deleted intentionally removes latest tombstones. |
| Deleted rows are still visible | Use --delete-mode exclude_deleted for active-only tables, or filter dpone_cdc_deleted = 0 downstream. |
| A downstream report sees stale data | Re-run the bounded CDC runtime tick first, then re-run materialization and compare rows_source_events. |
| Materialization is slow | Keep the append-only CDC log ordered by stream/key/position and schedule compaction only after the runtime offset is durable. |