Relationships
#859 Decouple prod ClickHouse from S3 (drop storage_policy=s3_main) + add a DDL migration path
Opened by keeb · 6/27/2026· Shipped 6/27/2026
Part of epic #847. Sibling of #853 (Unit 1 — land the local ClickHouse foundation). This issue tracks the prod changes that #853 deliberately does not make.
Problem
Prod ClickHouse (giga-swamp models/@giga-swamp/k8s-manifest/clickhouse.yaml) couples the serving engine to S3 in a way that isn't paying off at our scale, and leaves the schema un-migratable:
1. S3-coupled storage (storage_policy = 's3_main'). CH's own MergeTree parts live in a DO Space (clickhouse-data-swamp-club), fusing two concerns that should be independent failure domains:
- serving — hot, latency-sensitive, disposable
- durable log — the ndjson archive (
events-archive-swamp-club), the real system of record
Consequences: an S3 hiccup degrades queries, not just ingestion; the MergeTree merge engine pays continuous S3 request/egress cost (GET/PUT/DELETE on every merge, insert, and TTL drop — forever, proportional to ingest, independent of query load), plus range-GETs on every cache miss; and it obscures the real DR story ("CH rebuilds from S3" means replaying the ndjson archive, not the s3_disk parts). The dataset is small (~4M LowCardinality rows, a few hundred MB compressed) and TTL-capped at 2 years, so the auto-tiering benefit an s3 storage policy exists for never triggers. The retained PVC + the archive already provide durability — the s3 storage policy is pure tax. S3 should be break-glass only: append one PUT/batch to the archive, read it back solely on a rare rebuild.
2. Schema is a one-shot ConfigMap with no migration path. The init schema is a ConfigMap (clickhouse-init-schema) mounted at /docker-entrypoint-initdb.d, which ClickHouse runs only on an empty data dir. With the retained clickhouse-data PVC, the data dir is non-empty after first boot, so editing the ConfigMap does not re-apply to the running pod. It looks like GitOps-managed schema but is a one-time bootstrap. Epic #847 Units 4–5 (score_grants + materialized views) add DDL that must land in prod, and there is currently no tracked mechanism to apply it. Related: CH MATERIALIZED columns compute at INSERT time only and never backfill, so new dimensions over history require re-ingesting raw properties from the archive.
(Also: the manifest's top comment still claims "emptyDir, no PVC, schema runs on every restart" — stale and contradictory with the shipped retained-PVC spec.)
Proposed solution
- Drop
storage_policy = 's3_main'— serve from a plain local-disk MergeTree on the existing retainedclickhouse-dataPVC. Remove the now-unuseds3-storage.xmlfragment /clickhouse-s3-configConfigMap /clickhouse-s3-credsenv wiring if nothing else needs them. DR path becomes: lose the volume → replay from theevents-archive-swamp-clubndjson lake (backfill.sql). - Establish a real DDL migration path so Units 4–5 can ship schema changes to a running prod CH (the ConfigMap edit won't): e.g. a versioned-migration Job applied on deploy, or an idempotent
CREATE/ALTER … IF NOT EXISTSapply step that runs against the live server instead of relying on initdb-on-empty-dir. Include a backfill story for newMATERIALIZEDcolumns (re-ingest from the archive). - Fix the stale "emptyDir / no PVC" comment in the manifest.
Alternatives considered
- Keep
s3_mainas a cold tier — only worth it if the dataset outgrows local-disk economics; at current scale + 2-year TTL it won't for years, and you'd still need the archive for DR, so it's redundant. - Bundle this into #853 — rejected. #853 is the local-dev foundation (acceptance criteria are all local), lives in swamp-club2, and prod CH already runs. This is a different repo (giga-swamp) and a different blast radius.
Acceptance
- Prod CH serves from local disk;
storage_policy = 's3_main'and the S3 disk config are gone (or justified if retained). - A repeatable, idempotent mechanism applies schema/DDL changes to a running prod CH (verified by applying a no-op/additive change).
- DR is documented as archive-replay; S3 touched only on append + rebuild.
- Stale manifest comment corrected.
Links
- Epic: #847
- Sibling (local foundation): #853 — its local schema already omits
s3_main; this brings prod in line. - Prod manifest: giga-swamp
models/@giga-swamp/k8s-manifest/clickhouse.yaml
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.