Skip to content

MergeTree engine family

Factories

All MergeTree variants have typed factory functions in dbwarden.databases.clickhouse:

from dbwarden.databases.clickhouse import (
    merge_tree, replacing_merge_tree, replicated_merge_tree,
    summing_merge_tree, aggregating_merge_tree,
    collapsing_merge_tree, versioned_collapsing_merge_tree,
    graphite_merge_tree,
)
Factory Engine name Signature
merge_tree() MergeTree ()
replacing_merge_tree(ver?) ReplacingMergeTree (version_col: str \| None = None)
replicated_merge_tree(zk, replica, ...) ReplicatedMergeTree (zookeeper_path, replica_name, *args)
summing_merge_tree(...) SummingMergeTree (*columns: str)
aggregating_merge_tree() AggregatingMergeTree ()
collapsing_merge_tree(sign) CollapsingMergeTree (sign_col: str)
versioned_collapsing_merge_tree(sign, ver) VersionedCollapsingMergeTree (sign_col, version_col)
graphite_merge_tree(section?) GraphiteMergeTree (config_section: str = "default")

Example:

class Meta(CHTableMeta):
    ch = ch_table(
        engine=replicated_merge_tree(
            "/clickhouse/tables/events",
            "{replica}",
            "ver",
        ),
        order_by=["event_date", "id"],
    )

Generated DDL:

CREATE TABLE events (
    event_date Date,
    id Int64
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/events', '{replica}', 'ver')
ORDER BY (event_date, id)

MergeTreeSettings

ch_table(settings=MergeTreeSettings(...)) type-checks known MergeTree settings:

from dbwarden.databases.clickhouse import MergeTreeSettings

settings: MergeTreeSettings = {
    "index_granularity": 8192,
    "ttl_only_drop_parts": True,
    "min_bytes_for_wide_part": 10485760,
}

Boolean values are automatically converted to "0" / "1". Integer values are stringified. All values are rendered as strings before reaching the server.

What changes are allowed

Property Allowed
Engine variant Only with --force (full recreate)
ZK path / replica name Only with --force
Settings Any key-value via MODIFY SETTING (where supported by server)
ORDER BY Append-only
PARTITION BY Never

Additional model examples

ReplacingMergeTree with version column

class Product(Base):
    __tablename__ = "products"

    sku: Mapped[str] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
    price: Mapped[float] = mapped_column()
    updated_at: Mapped[datetime] = mapped_column()

    class Meta(CHTableMeta):
        ch = ch_table(
            engine=replacing_merge_tree(version_col="updated_at"),
            order_by="sku",
        )

Deduplicates by sku, keeping the row with the latest updated_at.

CollapsingMergeTree for mutable state

class OrderState(Base):
    __tablename__ = "order_state"

    order_id: Mapped[str] = mapped_column(primary_key=True)
    status: Mapped[str] = mapped_column()
    amount: Mapped[float] = mapped_column()
    sign: Mapped[int8] = mapped_column()

    class Meta(CHTableMeta):
        ch = ch_table(
            engine=collapsing_merge_tree(sign_col="sign"),
            order_by="order_id",
        )

Cancellations emit a row with sign = -1 that collapses with the original sign = 1.

SummingMergeTree

class DailySummary(Base):
    __tablename__ = "daily_summary"

    dt: Mapped[date] = mapped_column()
    product: Mapped[str] = mapped_column()
    revenue: Mapped[float] = mapped_column()
    units: Mapped[int] = mapped_column()

    class Meta(CHTableMeta):
        ch = ch_table(
            engine=summing_merge_tree("revenue"),
            order_by=["dt", "product"],
        )

revenue and units are summed automatically during merge.

GraphiteMergeTree

class GraphiteMetrics(Base):
    __tablename__ = "graphite_metrics"

    path: Mapped[str] = mapped_column()
    value: Mapped[float] = mapped_column()
    timestamp: Mapped[datetime] = mapped_column()

    class Meta(CHTableMeta):
        ch = ch_table(
            engine=graphite_merge_tree(config_section="rollup_default"),
            order_by=["path", "timestamp"],
            partition_by="toYYYYMM(timestamp)",
        )

Rollback behavior

Engine changes with --force trigger the full recreate pipeline. See Safety.