prole/demo/ci-migration/01_schema_setup.sql

131 lines
5.6 KiB
SQL

-- 01_schema_setup.sql
-- Creates the demo schema, legacy table, OLTP workload table,
-- and all monitoring views for the CI migration danger demo.
--
-- Run as superuser (postgres) against knoe-db.
-- Safe to re-run: all objects use IF NOT EXISTS / CREATE OR REPLACE.
CREATE SCHEMA IF NOT EXISTS demo;
-- ── Legacy table (source of the backfill) ────────────────────────────────────
-- Represents the old non-partitioned ci_job_artifacts table on a 10 TB GitLab
-- DB. In our scaled demo we'll fill this with ~5 million rows (~1 GB).
CREATE TABLE IF NOT EXISTS demo.ci_job_artifacts_legacy (
id BIGSERIAL PRIMARY KEY,
job_id BIGINT NOT NULL,
project_id BIGINT NOT NULL,
file_type SMALLINT NOT NULL DEFAULT 0,
size_bytes BIGINT,
file_store SMALLINT NOT NULL DEFAULT 1,
checksum TEXT,
created_at TIMESTAMP NOT NULL,
expire_at TIMESTAMP,
locked BOOLEAN DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_legacy_created_at
ON demo.ci_job_artifacts_legacy (created_at);
-- ── OLTP workload table (concurrent traffic during migration) ─────────────────
-- Simulates background CI pipeline activity during the migration window.
-- UPDATEs to this table generate dead tuples that autovacuum must clean.
-- When a long-running migration transaction holds an old snapshot, autovacuum
-- is blocked and dead tuples pile up — this is the MVCC bloat we'll observe.
CREATE TABLE IF NOT EXISTS demo.ci_build_status (
id BIGSERIAL PRIMARY KEY,
job_id BIGINT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- ── Monitoring views ──────────────────────────────────────────────────────────
CREATE OR REPLACE VIEW demo.mvcc_bloat_monitor AS
SELECT
relname AS table_name,
n_live_tup AS live_rows,
n_dead_tup AS dead_rows,
ROUND(n_dead_tup::NUMERIC / NULLIF(n_live_tup, 0) * 100, 2) AS dead_pct,
last_autovacuum,
last_autoanalyze,
pg_size_pretty(pg_total_relation_size('demo.' || relname)) AS total_size,
pg_size_pretty(pg_relation_size('demo.' || relname)) AS table_size
FROM pg_stat_user_tables
WHERE schemaname = 'demo'
ORDER BY n_dead_tup DESC;
-- Shows any transaction open longer than 5 seconds — the migration blocker.
CREATE OR REPLACE VIEW demo.long_running_tx AS
SELECT
pid,
now() - xact_start AS tx_duration,
now() - query_start AS query_duration,
state,
wait_event_type,
wait_event,
left(query, 200) AS query_snippet,
backend_type,
application_name
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
AND now() - xact_start > INTERVAL '5 seconds'
AND pid <> pg_backend_pid()
ORDER BY tx_duration DESC;
-- WAL generation tracker — shows how fast WAL is growing.
-- Run before and after to calculate the delta.
CREATE OR REPLACE VIEW demo.wal_progress AS
SELECT
pg_current_wal_lsn() AS current_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0'::pg_lsn)) AS total_wal,
(SELECT count(*) FROM pg_ls_waldir()) AS wal_segment_count,
pg_size_pretty((SELECT sum(size) FROM pg_ls_waldir())) AS wal_dir_size;
-- Lock wait graph — shows who is blocking whom.
CREATE OR REPLACE VIEW demo.lock_waits AS
SELECT
blocked_a.pid AS blocked_pid,
blocking_a.pid AS blocking_pid,
now() - blocked_a.query_start AS wait_duration,
left(blocked_a.query, 120) AS blocked_query,
left(blocking_a.query, 120) AS blocking_query,
blocked_l.locktype,
blocked_l.relation::regclass AS locked_relation
FROM pg_catalog.pg_locks blocked_l
JOIN pg_catalog.pg_locks blocking_l
ON blocking_l.locktype = blocked_l.locktype
AND blocking_l.relation = blocked_l.relation
AND blocking_l.granted
AND NOT blocked_l.granted
JOIN pg_stat_activity blocked_a ON blocked_a.pid = blocked_l.pid
JOIN pg_stat_activity blocking_a ON blocking_a.pid = blocking_l.pid
ORDER BY wait_duration DESC;
-- Autovacuum activity — shows if autovacuum is currently running or stuck.
CREATE OR REPLACE VIEW demo.autovacuum_status AS
SELECT
pid,
now() - xact_start AS running_for,
query AS vacuum_query,
wait_event_type,
wait_event
FROM pg_stat_activity
WHERE query LIKE 'autovacuum:%'
ORDER BY xact_start;
-- Oldest transaction horizon — the horizon that blocks dead tuple cleanup.
-- When this is far in the past and dead tuples are climbing, we have MVCC bloat.
CREATE OR REPLACE VIEW demo.xmin_horizon AS
SELECT
pid,
backend_xmin AS xmin,
age(backend_xmin) AS xmin_age,
now() - xact_start AS tx_age,
state,
left(query, 120) AS query_snippet
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY age(backend_xmin) DESC;
\echo 'Schema and views created. Run 02_generate_data.sql next.'