Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
AssigneesNone

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

  1. Drop storage_policy = 's3_main' — serve from a plain local-disk MergeTree on the existing retained clickhouse-data PVC. Remove the now-unused s3-storage.xml fragment / clickhouse-s3-config ConfigMap / clickhouse-s3-creds env wiring if nothing else needs them. DR path becomes: lose the volume → replay from the events-archive-swamp-club ndjson lake (backfill.sql).
  2. 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 EXISTS apply step that runs against the live server instead of relying on initdb-on-empty-dir. Include a backfill story for new MATERIALIZED columns (re-ingest from the archive).
  3. Fix the stale "emptyDir / no PVC" comment in the manifest.

Alternatives considered

  • Keep s3_main as 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.
  • 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
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPEDTRIAGE+ 5 MOREREVIEW+ 2 MOREPR_LINKEDCOMPLETE

Shipped

6/27/2026, 7:14:38 AM

Click a lifecycle step above to view its details.

03Sludge Pulse

Sign in to post a ripple.