Lakehouse ClickHouse With 3 Signed SLOs: Proven DAPE Platform

A lakehouse ClickHouse estate is easy to buy and hard to own. You can license certified builds and a support desk, or you can pay per-second for a managed cloud that scales compute against object storage. What neither purchase gives you is a written commitment that yesterday’s events are queryable within 15 minutes on the warm tier, that a Q1 dashboard class holds p95 under 300 ms on the hot tier, and that the blended cost per terabyte-month bends downward at the 7-day and 90-day boundaries.

ChistaDATA DAPE (Data Analytics Platform Engineering) sells exactly that: lakehouse ClickHouse delivered as an engineered platform with signed SLOs for freshness across tiers, p95/p99 latency per tier and query class, and a measured cost curve.

This post explains what those three SLOs are, how they are measured from system.* tables, what the platform has to look like for them to be achievable on 100% open-source ClickHouse 26.3 LTS, and — factually, from their own published material — why the two vendors most often compared with us do not sell the same thing.

The finding first. Altinity sells software plus support (Stable Builds, Altinity.Cloud, Project Antalya) and ClickHouse Inc. sells cloud consumption (compute and storage metered separately under an uptime-only SLA). Both are legitimate products. Neither contract binds the vendor to data freshness, per-tier latency percentiles, or a cost-per-terabyte trajectory. DAPE does, and the SLO ledger lives inside the customer’s own ClickHouse cluster.

What lakehouse ClickHouse means in a DAPE engagement

In our usage, lakehouse ClickHouse is a three-tier topology in which ClickHouse MergeTree is the serving engine for hot and warm data and open table formats on object storage — Apache Iceberg with Parquet — hold the cold horizon. The tiers are not three products; they are one storage policy, one TTL clause, and one export path, and every one of them carries its own SLO envelope.

Lakehouse ClickHouse three-tier architecture with hot NVMe MergeTree, warm S3-backed MergeTree, cold Iceberg on S3, and signed SLO telemetry sources
Figure 1 — Lakehouse ClickHouse as DAPE delivers it: three tiers, three SLO envelopes, telemetry sourced from system.* tables. Envelope values are illustrative.
  • Hot tier — ReplicatedMergeTree on local NVMe, 0–7 days, serves the latency-critical query classes. Sort key and partitioning are validated against system.query_log predicates, never against the data model alone.
  • Warm tier — the same MergeTree table, parts moved by TTL … TO VOLUME onto an S3-backed disk fronted by a local cache. This is where most of the retention lives and where the cost curve first bends.
  • Cold tier — Parquet files under an Iceberg catalog on S3, queried through the IcebergS3 engine for audit, backfill and compliance classes. ClickHouse is treated as an Iceberg reader in production designs; upstream Iceberg INSERT support has existed since 25.8 behind an experimental/beta setting and is not on the signed path.

The reason to write lakehouse ClickHouse down as an engineered platform rather than a reference architecture is that each tier fails differently. Hot-tier freshness fails on merge backlog and part-count explosions; warm-tier latency fails on S3 GET amplification when the cache is undersized or the sort key is wrong for the class; cold-tier freshness fails silently when an export job misses a partition. An SLO per tier makes each failure mode a measured, owned number.

Why software-plus-support and cloud consumption stop at the lakehouse ClickHouse platform boundary

This is not a criticism of either vendor; it is a description of what their commercial form can and cannot commit to. We validated each statement against the vendor’s own pages in August 2026.

Commercial formWhat is soldWhat the contract commits toWhat it cannot sign
Altinity Stable Builds + supportCertified ClickHouse binaries maintained for up to three years; enterprise support subscriptions; Altinity.Cloud and BYOC; Project Antalya for Iceberg/Parquet tieringBuild lifecycle, security fixes, support response — the software and the people behind itFreshness, per-tier percentiles, or cost curve on your workload — the vendor does not own your schema, ingestion shape, or storage policy
ClickHouse Cloud consumptionCompute and storage metered separately with autoscaling; SharedMergeTree; ClickPipes; WarehousesAn uptime SLA with service credits for HA-configured servicesQuery latency or data freshness — the SLA text scopes itself to availability; the cost trajectory is a function of your consumption, not a commitment
ChistaDATA DAPEThe engineered platform: schema, tiering, ingestion, Keeper, observability and the SLO ledger — on 100% open-source ClickHouse, in your VPC or data centreSigned freshness, p95/p99 per tier and class, and a cost curve re-baselined quarterly, with error-budget policy and creditsAnything we have not measured on your estate first — the first 30 days establish the baseline the numbers are signed against

Two technical facts underpin the table and explain why a lakehouse ClickHouse SLO has to be sold with the platform. First, SharedMergeTree is a different storage engine from ReplicatedMergeTree, so a Cloud sizing model and its merge behaviour do not transfer to a self-hosted estate; a vendor whose SLA is written for one engine cannot underwrite the other.

Second, an SLO on latency is only meaningful once a query class is defined against a sort key, a projection strategy and a tier — decisions that sit in the customer’s schema, which is precisely the layer a software licence or a consumption meter does not touch.

The three signed lakehouse ClickHouse SLOs, as engineering objects

Every DAPE lakehouse ClickHouse contract carries a machine-readable SLO schedule. The excerpt below is the shape; the numbers in a real schedule are the customer’s measured values after the baseline period, pinned to the LTS they were measured on.

# CD-SLO-2026-08-<CUSTOMER> — signed SLO schedule (excerpt)
platform: lakehouse-clickhouse
clickhouse_version: "26.3 LTS"          # every SLO is pinned to the LTS it was measured on
measurement_window: 28d                 # rolling; attested monthly
tiers:
  hot:
    storage: nvme_replicated_mergetree
    retention: 0-7d
    freshness_slo:  { target_seconds: 30,   objective: 0.999 }   # 99.9% of 60 s samples ≤ 30 s
    latency_slo:
      class_q1_dashboards: { p95_ms: 300,  p99_ms: 800,   objective: 0.99 }
      class_q2_adhoc:      { p95_ms: 2000, p99_ms: 5000,  objective: 0.95 }
    cost_curve: { usd_per_tb_month: "<measured>", usd_per_1k_queries_q1: "<measured>" }
  warm:
    storage: s3_backed_mergetree
    retention: 7-90d
    freshness_slo:  { target_seconds: 900,  objective: 0.999 }
    latency_slo:
      class_q2_adhoc:      { p95_ms: 2000, p99_ms: 5000,  objective: 0.95 }
    cost_curve: { usd_per_tb_month: "<measured>" }
  cold:
    storage: iceberg_parquet_s3
    retention: 90d-7y
    freshness_slo:  { target_seconds: 86400, objective: 0.99 }
    latency_slo:
      class_q3_audit:      { p95_ms: 30000, p99_ms: 90000, objective: 0.95 }
    cost_curve: { usd_per_tb_month: "<measured>" }
error_budget_policy:
  burn_rate_alerts: [ { window: 2h, factor: 14.4 }, { window: 24h, factor: 3 } ]
  exhausted: "feature freeze on the tier; ChistaDATA engineering time re-prioritised to the SLO"
attestation:
  evidence_tables: [ slo.sli_samples, slo.slo_rollup_28d ]
  signed_by: [ customer_data_platform_owner, chistadata_engagement_lead ]

SLO 1 — Freshness across tiers

In a lakehouse ClickHouse platform, freshness is defined per tier as the age of the newest row that is visible in that tier’s active parts — for the hot tier relative to now, for the warm tier relative to the MOVE TTL boundary — sampled every 60 seconds. It is not an ingestion-pipeline metric borrowed from Kafka lag; it is measured where the query runs. On 26.3 LTS async_insert is enabled by default, so the hot-tier freshness budget must explicitly absorb the buffer-to-flush interval, and wait_for_async_insert = 1 is the durability contract we state to application teams.

-- SLI: freshness per tier. Hot lag = now() - newest event visible in hot parts.
-- Warm lag = how far the warm tier trails the 7-day MOVE TTL boundary (0 when the
-- move pipeline is keeping up). Parts are resolved to disks via the _part virtual
-- column, so the number is measured where the query runs, not in the pipeline.
WITH
    now64(3)                       AS ts,
    ts - INTERVAL 7 DAY            AS warm_boundary
SELECT
    p.tier,
    if(p.tier = 'hot',
       ts - max(e.event_time),
       greatest(0, warm_boundary - max(e.event_time)))         AS freshness_lag_s,
    uniqExact(e._part)                                          AS active_parts
FROM analytics.events_local AS e
INNER JOIN
(
    SELECT
        name,
        multiIf(disk_name = 'hot_nvme', 'hot',
                disk_name = 'warm_s3_cache', 'warm', 'other')  AS tier
    FROM system.parts
    WHERE database = 'analytics'
      AND table    = 'events_local'
      AND active
) AS p ON e._part = p.name
WHERE e.event_date >= today() - 9                             -- prune to the boundary window
GROUP BY p.tier;

-- Ingest-side freshness for async inserts (26.3 LTS default-on):
-- time from first buffered row to flush, i.e. the latency the SLO must absorb.
SELECT
    quantileTDigest(0.95)(flush_time - event_time)  AS p95_flush_s,
    quantileTDigest(0.99)(flush_time - event_time)  AS p99_flush_s,
    countIf(status != 'Ok')                         AS failed_flushes
FROM system.asynchronous_insert_log
WHERE database = 'analytics'
  AND table    = 'events_local'
  AND event_time >= now() - INTERVAL 1 HOUR;

Warm-tier freshness has a second component: the time between a part becoming eligible for MOVE TTL and its arrival on the S3 volume, visible in system.moves and system.part_log with event_type = 'MovePart'. Cold-tier freshness is the age of the newest committed Iceberg snapshot relative to the newest MergeTree partition that has aged past the warm boundary. Each of the three is a number, each has a target, and each has an objective (the fraction of samples that must meet the target).

SLO 2 — p95 and p99 per tier and per query class

A lakehouse ClickHouse latency SLO without a query class is a marketing number. DAPE requires every client to tag its queries with log_comment, which flows into system.query_log unchanged and lets the SLI be sliced by class and tier. Percentiles are computed with quantileTDigest over the raw log across all replicas, not from a sampled dashboard.

-- SLI: p95/p99 per query class. Class comes from the mandatory log_comment
-- every DAPE client sets (e.g. SETTINGS log_comment = 'class=q1;tier=hot').
SELECT
    extract(log_comment, 'class=(\\w+)')                        AS query_class,
    extract(log_comment, 'tier=(\\w+)')                         AS tier,
    count()                                                     AS queries,
    quantileTDigest(0.95)(query_duration_ms)                    AS p95_ms,
    quantileTDigest(0.99)(query_duration_ms)                    AS p99_ms,
    sum(read_bytes) / 1e9                                       AS read_gb,
    sum(ProfileEvents['S3GetObject'])                           AS s3_gets,
    sum(ProfileEvents['SelectedMarks'])                         AS selected_marks
FROM clusterAllReplicas('{cluster}', system.query_log)
WHERE type       = 'QueryFinish'
  AND query_kind = 'Select'
  AND event_time >= now() - INTERVAL 1 HOUR
  AND log_comment LIKE 'class=%'
GROUP BY query_class, tier
ORDER BY tier, query_class;

The two ProfileEvents in that query are the diagnostic edge of the SLO. A warm-tier class whose S3GetObject count climbs while SelectedMarks stays flat is a cache-sizing problem; a hot-tier class whose SelectedMarks climbs is a sort-key or projection problem. Both are engineering actions we own under the contract, and both are diagnosed in the same table the SLO is measured from — the approach we describe in ClickHouse query optimisation for petabyte-scale analytics.

SLO 3 — The cost curve

The lakehouse ClickHouse cost curve is the blended dollars per terabyte-month as a function of data age, plus dollars per thousand queries for each class. It is derived from system.parts compressed bytes per disk joined to the unit prices on the customer’s own cloud invoice. It is signed as a shape with re-baselining every quarter, because unit prices, compression ratios and the hot/warm boundary all move.

-- Cost curve input: compressed bytes per tier, joined to the unit price per disk.
-- Unit prices come from the customer's invoice; nothing here is list-price arithmetic.
SELECT
    p.disk_name                                              AS disk,
    round(sum(p.bytes_on_disk) / 1e12, 3)                    AS tb_on_disk,
    round(sum(p.data_uncompressed_bytes) / sum(p.bytes_on_disk), 2) AS compression_ratio,
    u.usd_per_tb_month,
    round(tb_on_disk * u.usd_per_tb_month, 2)                AS usd_month
FROM clusterAllReplicas('{cluster}', system.parts) AS p
INNER JOIN slo.unit_prices AS u ON u.disk_name = p.disk_name
WHERE p.active
  AND p.database = 'analytics'
GROUP BY disk, u.usd_per_tb_month
ORDER BY usd_month DESC;
Illustrative lakehouse ClickHouse cost curve showing dollars per terabyte-month falling across hot NVMe, warm S3-backed MergeTree and cold Iceberg tiers
Figure 2 — Illustrative cost curve. The step positions (7 days, 90 days) are the tier boundaries; moving a boundary is an engineering decision with a measured latency and freshness consequence.

The point of signing a lakehouse ClickHouse cost curve rather than a single number is that the hot/warm boundary is the most expensive knob on the platform. Pulling it from 7 days to 3 days halves hot-tier NVMe spend and pushes the Q2 ad-hoc class onto S3 reads; the latency SLO for that class decides whether the move is allowed. The three SLOs constrain each other on purpose.

How lakehouse ClickHouse SLOs are measured, attested and enforced

Signed SLO measurement loop for lakehouse ClickHouse: SLI capture from system tables, rollup, error budget, engineering action, monthly attestation
Figure 3 — The SLO loop. Evidence tables are ReplicatedMergeTree inside the customer estate; both parties attest against identical data.

The lakehouse ClickHouse SLO ledger is a ClickHouse table. It is replicated, partitioned by month, retained for thirteen months so a full year of attestations is always reproducible, and it is the only source both signatures refer to.

-- SLO ledger and 28-day error budget. The ledger is a ReplicatedMergeTree table
-- in the customer's own estate, so both parties attest against identical evidence.
CREATE TABLE slo.sli_samples ON CLUSTER '{cluster}'
(
    sampled_at   DateTime('UTC'),
    tier         LowCardinality(String),
    sli          LowCardinality(String),    -- 'freshness_s' | 'p95_ms' | 'p99_ms'
    query_class  LowCardinality(String),
    value        Float64,
    target       Float64,
    good         UInt8 MATERIALIZED value <= target
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/slo/sli_samples', '{replica}')
PARTITION BY toYYYYMM(sampled_at)
ORDER BY (tier, sli, query_class, sampled_at)
TTL sampled_at + INTERVAL 13 MONTH DELETE
SETTINGS index_granularity = 8192;

-- Remaining error budget per (tier, sli, class) over the attestation window.
WITH 0.999 AS objective
SELECT
    tier,
    sli,
    query_class,
    count()                                     AS samples,
    countIf(good = 0)                           AS bad_samples,
    round(1 - bad_samples / samples, 5)         AS achieved,
    round((1 - objective) * samples)            AS budget_samples,
    budget_samples - bad_samples                AS budget_remaining
FROM slo.sli_samples
WHERE sampled_at >= now() - INTERVAL 28 DAY
GROUP BY tier, sli, query_class
ORDER BY budget_remaining ASC;

Lakehouse ClickHouse error-budget policy follows the multi-window burn-rate pattern documented in the Google SRE Workbook: a fast window (2 hours at 14.4× burn) pages, a slow window (24 hours at 3×) opens a ticket. When a tier’s budget is exhausted, the contract triggers a feature freeze on that tier and reprioritises ChistaDATA engineering time to the SLO. This is the same discipline we run as Data SRE for ClickHouse, applied to a commercial commitment.

What a lakehouse ClickHouse platform has to look like for the SLOs to be achievable

Signing lakehouse ClickHouse numbers is only responsible if the platform is engineered to hit them. The following are the non-negotiable design elements in a DAPE lakehouse ClickHouse build on 26.3 LTS.

Storage policy and TTL define the tier boundary

<!-- /etc/clickhouse-server/config.d/storage.xml  (restart required for new disks) -->
<clickhouse>
  <storage_configuration>
    <disks>
      <hot_nvme>
        <path>/var/lib/clickhouse/hot/</path>
      </hot_nvme>
      <warm_s3>
        <type>s3</type>
        <endpoint>https://s3.${AWS_REGION}.amazonaws.com/${CH_WARM_BUCKET}/${CH_CLUSTER}/{replica}/</endpoint>
        <access_key_id>${CH_S3_ACCESS_KEY}</access_key_id>
        <secret_access_key>${CH_S3_SECRET_KEY}</secret_access_key>
        <metadata_path>/var/lib/clickhouse/disks/warm_s3/</metadata_path>
      </warm_s3>
      <warm_s3_cache>
        <type>cache</type>
        <disk>warm_s3</disk>
        <path>/var/lib/clickhouse/cache/warm_s3/</path>
        <max_size>400Gi</max_size>
      </warm_s3_cache>
    </disks>
    <policies>
      <tiered_hot_warm>
        <volumes>
          <hot><disk>hot_nvme</disk></hot>
          <warm><disk>warm_s3_cache</disk></warm>
        </volumes>
        <move_factor>0.10</move_factor>
      </tiered_hot_warm>
    </policies>
  </storage_configuration>
</clickhouse>
-- Hot + warm tiers in one table; the tier boundary is a TTL expression, not an ETL job.
CREATE TABLE analytics.events_local ON CLUSTER '{cluster}'
(
    event_date     Date                 CODEC(DoubleDelta, ZSTD(1)),
    event_time     DateTime64(3, 'UTC') CODEC(DoubleDelta, ZSTD(1)),
    tenant_id      UInt32,
    event_type     LowCardinality(String),
    entity_id      UInt64,
    payload        JSON,                                   -- JSON is GA since 25.3
    INDEX idx_entity entity_id TYPE bloom_filter(0.01) GRANULARITY 4
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/analytics/events_local', '{replica}')
PARTITION BY toYYYYMMDD(event_date)
ORDER BY (tenant_id, event_type, event_time)
TTL event_date + INTERVAL 7 DAY  TO VOLUME 'warm',
    event_date + INTERVAL 90 DAY DELETE                    -- cold copy is exported before this fires
SETTINGS
    storage_policy            = 'tiered_hot_warm',
    index_granularity         = 8192,
    min_bytes_for_wide_part   = 10485760,
    ttl_only_drop_parts       = 1;                         -- whole-part drops: cheap, predictable

-- Cold tier: Iceberg on S3, read-only from ClickHouse in production designs.
-- Iceberg INSERT support exists upstream since 25.8 behind an experimental/beta
-- setting; the DAPE cold path uses Parquet export + catalog commit instead.
CREATE TABLE analytics.events_cold
ENGINE = IcebergS3(
    'https://s3.${AWS_REGION}.amazonaws.com/${CH_COLD_BUCKET}/warehouse/analytics/events/',
    '${CH_S3_ACCESS_KEY}', '${CH_S3_SECRET_KEY}',
    'Parquet'
);

The S3 disk is wrapped in a cache disk sized from the warm-class working set (read_bytes per class over 7 days from system.query_log), not from a rule of thumb. ttl_only_drop_parts = 1 keeps the 90-day delete cheap. Zero-copy replication is not used: allow_remote_fs_zero_copy_replication remains experimental upstream, so warm-tier replicas hold their own S3 objects and the cost curve is computed on that basis. Full storage-policy semantics are in the MergeTree documentation.

Ingestion shape is designed with the freshness SLO

  • Batches of 10k–500k rows per INSERT, enforced server-side by async_insert on 26.3 LTS, with async_insert_busy_timeout_ms set below the hot-tier freshness target minus merge headroom.
  • Dedup semantics derived per estate from system.asynchronous_insert_log and system.part_log before any exactly-once claim; 26.x changed the default dedup surface and the docs are not fully reconciled.
  • Kafka via an external consumer or sink connector rather than the Kafka table engine, which remains labelled experimental; the design pattern is in ClickHouse Kafka real-time analytics.

Query classes choose projections or parallel replicas, not both

Projections remain incompatible with parallel replicas as of 26.7. Each query class in the SLO schedule therefore declares which mechanism serves it, and the choice is verified with EXPLAIN PIPELINE before the class is signed. Hot-tier dashboard classes typically get narrow index-shaped projections; warm-tier ad-hoc classes lean on parallel replicas across the S3-backed replicas.

Keeper is sized and drilled as tier-0

The 26.2/26.3 dedup and async-insert default changes moved Keeper load into the first-order risk register. Every DAPE estate baselines system.zookeeper_log request rates and system.zookeeper_info before any upgrade and re-compares within one hour after. Keeper is upgraded first and separately; a freshness SLO cannot survive a coordination outage.

Cold tier: reader in production, writer under labelled acceptance

In the lakehouse ClickHouse cold tier, data reaches Iceberg through a Parquet export and catalog commit, with a reconciliation query comparing count() and max(event_time) per partition between the warm MergeTree table and the IcebergS3 engine view before the MergeTree partition is allowed to age out. For estates that want MergeTree-to-Iceberg tiering natively, Altinity Project Antalya is the credible open-source path and we will run it where a customer accepts its experimental flags; the signed cold-tier SLO does not depend on it.

Where DAPE lakehouse ClickHouse does not apply, and what we do not sign

  • Estates on ClickHouse Cloud. SharedMergeTree behaviour, Warehouses and ClickPipes are outside our measurement surface. We will design the OSS-portable half of such an estate and state the exit cost of the cloud-only half, but we do not sign SLOs on an engine we do not operate.
  • Unclassified workloads. If a customer cannot or will not tag queries by class, there is no latency SLO — only the freshness and cost SLOs are offered until classification is in place.
  • Numbers before baseline. The first 30 days of any engagement are measurement. Targets proposed before that period are labelled illustrative, as they are in every figure above.
  • Versions we have not soaked. SLOs are pinned to an LTS. A move to a non-LTS stable resets the attestation window until the platform has soaked on it.
  • Regulated builds. Where FIPS-validated binaries are contractual, the available Altinity Stable variants do not currently include 25.8 or 26.3; that becomes a compliance-versus-currency decision taken with the customer before any number is signed.

Lakehouse ClickHouse with signed SLOs: frequently asked questions

Is DAPE a competitor to Altinity or ClickHouse Cloud?

DAPE lakehouse ClickHouse occupies a different layer. Altinity’s builds and operator, and ClickHouse Inc.’s open-source engine, are inputs to a DAPE platform; we operate Altinity’s clickhouse-operator and clickhouse-backup where they fit. What DAPE adds is the engineered platform above them and the signed SLOs that a software licence or a consumption meter cannot carry.

What versions of ClickHouse does the signed platform run on?

A DAPE lakehouse ClickHouse platform tracks the current LTS — 26.3 LTS as of August 2026, with 25.8 LTS reaching upstream end of support at the end of the month. Change-frozen estates may run Altinity Stable 25.8 under its extended lifecycle; the SLO schedule records the exact build it was measured on.

Can the cost curve include the ClickHouse cloud bill for the warm and cold tiers?

Yes. The slo.unit_prices table is loaded from the customer’s actual invoice lines for NVMe, S3 storage, S3 requests and egress, and the curve is recomputed monthly against those prices. The curve is signed as a shape and re-baselined quarterly.

How is a missed SLO handled commercially?

Error-budget exhaustion triggers a feature freeze on the affected tier and reprioritises ChistaDATA engineering to the SLO. Persistent misses across an attestation period carry service credits defined in the contract. Both parties attest against the same ledger table, so there is no dispute over whose dashboard is right.

Next step

If you run, or are about to build, a lakehouse ClickHouse estate and want freshness, per-tier percentiles and a cost curve you can sign rather than hope for, start with a baseline. ChistaDATA ClickHouse consulting scopes the 30-day measurement period; ClickHouse managed services carries the platform and its SLOs after that. Standing caveat for everything above: test every change in staging before production, keep a verified restore drill in the calendar, and treat the numbers in this post as illustrative until they have been measured on your estate.

About ChistaDATA Inc. 252 Articles
We are an full-stack ClickHouse infrastructure operations Consulting, Support and Managed Services provider with core expertise in performance, scalability and data SRE. Based out of California, Our consulting and support engineering team operates out of San Francisco, Vancouver, London, Germany, Russia, Ukraine, Australia, Singapore and India to deliver 24*7 enterprise-class consultative support and managed services. We operate very closely with some of the largest and planet-scale internet properties like PayPal, Garmin, Honda cars IoT project, Viacom, National Geographic, Nike, Morgan Stanley, American Express Travel, VISA, Netflix, PRADA, Blue Dart, Carlsberg, Sony, Unilever etc