| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- -- 007_clickhouse.up.sql
- -- M7: ClickHouse schema for the deliveries archive.
- -- M8: + deliveries_dlq_archive (DLQ) with 2y TTL.
- --
- -- ClickHouse is columnar and prefers wide tables with
- -- per-column compression. The schema mirrors the Postgres
- -- `deliveries` table one-to-one for v1; a future M7.5 can
- -- strip unused fields (e.g. drop the JSONB payload blob
- -- if we never query into it in CH).
- --
- -- The DDL below is designed to be runnable from the
- -- archiverd's first-run setup hook. It's also what the
- -- M7 smoke executes by hand via curl http://clickhouse:8123/.
- --
- -- Note on types:
- -- - `id` is BIGINT in Postgres (BIGSERIAL). In CH we
- -- keep BIGINT — CH's UInt64 is the closest match,
- -- but for an archive that doesn't reference id
- -- anywhere else, BIGINT is fine.
- -- - `payload` is JSONB in Postgres. We store it as a
- -- CH String. The Go side marshals to JSON text
- -- before INSERT. CH's JSON type is still experimental
- -- in 24.x; String is the safe choice.
- -- - `status` is a small enum ('pending','sent','failed','dlq').
- -- In CH we use LowCardinality(String) — the column
- -- takes ~0.5 bytes per row for the dictionary.
- CREATE DATABASE IF NOT EXISTS ba_archive;
- -- ── M7: live deliveries archive ─────────────────────────────────
- -- 1-year retention. Rows that age out of the Timescale
- -- 7d hot window land here; after 1y CH drops the
- -- partition. Operators can override via
- -- ALTER TABLE ba_archive.deliveries_archive MODIFY TTL ...
- CREATE TABLE IF NOT EXISTS ba_archive.deliveries_archive (
- id BIGINT,
- alert_id String,
- company_id String,
- individual_id String,
- channel LowCardinality(String),
- target String,
- status LowCardinality(String),
- attempts UInt32,
- last_error String,
- payload String, -- raw JSON
- created_at DateTime64(3, 'UTC'),
- sent_at Nullable(DateTime64(3, 'UTC')),
- next_attempt_at Nullable(DateTime64(3, 'UTC')),
- -- The source postgres row's last-update marker. We
- -- set this to the wall-clock time at insert. Useful
- -- for "show me everything archived in the last 24h".
- archived_at DateTime DEFAULT now()
- ) ENGINE = MergeTree
- PARTITION BY toYYYYMM(created_at)
- ORDER BY (company_id, created_at, id)
- TTL toDateTime(created_at) + INTERVAL 365 DAY;
- -- A MATERIALIZED VIEW that aggregates per-company,
- -- per-day delivery counts. Used by M9 dashboards.
- CREATE MATERIALIZED VIEW IF NOT EXISTS
- ba_archive.deliveries_per_company_daily_mv
- ENGINE = SummingMergeTree
- PARTITION BY toYYYYMM(day)
- ORDER BY (company_id, day, channel, status)
- AS
- SELECT
- company_id,
- toDate(created_at) AS day,
- channel,
- status,
- count() AS n
- FROM ba_archive.deliveries_archive
- GROUP BY company_id, day, channel, status;
- -- ── M8: DLQ archive ────────────────────────────────────────────
- -- 2-year retention (vs 1y for live). DLQ entries are
- -- forensic data — operators want them around longer
- -- when triaging a regression. Mirrors the live
- -- deliveries_archive column shape minus the retry-
- -- scheduling columns (sent_at, next_attempt_at) which
- -- the DLQ doesn't carry.
- CREATE TABLE IF NOT EXISTS ba_archive.deliveries_dlq_archive (
- id BIGINT,
- alert_id String,
- company_id String,
- individual_id String,
- channel LowCardinality(String),
- target String,
- status LowCardinality(String),
- attempts UInt32,
- last_error String,
- payload String, -- raw JSON, the original NATS envelope
- created_at DateTime64(3, 'UTC'),
- archived_at DateTime DEFAULT now()
- ) ENGINE = MergeTree
- PARTITION BY toYYYYMM(created_at)
- ORDER BY (company_id, created_at, id)
- TTL toDateTime(created_at) + INTERVAL 730 DAY;
- -- Per-company daily DLQ counts. Used by the M9
- -- observability stack to surface DLQ rate per tenant.
- CREATE MATERIALIZED VIEW IF NOT EXISTS
- ba_archive.deliveries_dlq_per_company_daily_mv
- ENGINE = SummingMergeTree
- PARTITION BY toYYYYMM(day)
- ORDER BY (company_id, day, channel)
- AS
- SELECT
- company_id,
- toDate(created_at) AS day,
- channel,
- count() AS n
- FROM ba_archive.deliveries_dlq_archive
- GROUP BY company_id, day, channel;
|