Implement Phase 0b base project against the frozen test contract
All seven services (gateway, ledger, fraud, sanctions, insights, notifier, audit), db/schema.sql plus optional_indexes.sql, scripts/seed.py and smoke.py, the gateway-served frontend, and docker-compose.yml. Plain-text stdlib logging and a weak manual X-Request-Id only -- no observability yet, by design, per test_no_observability.py. Implements all 13 named chaos-flag pathologies from docs/PATHOLOGIES.md behind Redis-backed runtime flags. make smoke and make test-contract pass; make test-baseline and make test-pathology pass except for a handful of load-sensitive tests documented in tests/README.md rather than papered over.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- Applied only by `make drop-index`. Reverses db/optional_indexes.sql.
|
||||
|
||||
DROP INDEX IF EXISTS transactions_account_created_idx;
|
||||
DROP INDEX IF EXISTS transactions_category_idx;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Applied only by `make fix-index` (docs/phase-0-base-project-spec.md §5, §12).
|
||||
-- These are the indexes deliberately omitted from db/schema.sql.
|
||||
|
||||
CREATE INDEX IF NOT EXISTS transactions_account_created_idx ON transactions (account_id, created_at);
|
||||
CREATE INDEX IF NOT EXISTS transactions_category_idx ON transactions (category_id);
|
||||
@@ -0,0 +1,80 @@
|
||||
-- Ledgerline schema. Idempotent: safe to run against an existing database.
|
||||
-- Index policy is deliberate -- see docs/phase-0-base-project-spec.md §5.
|
||||
-- Do NOT add transactions(account_id, created_at) or transactions(category_id)
|
||||
-- here; those live in db/optional_indexes.sql, applied only by `make fix-index`.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id UUID PRIMARY KEY,
|
||||
holder_name TEXT NOT NULL,
|
||||
currency CHAR(3) NOT NULL,
|
||||
balance_minor BIGINT NOT NULL,
|
||||
is_hot BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS merchant_categories (
|
||||
id SMALLINT PRIMARY KEY,
|
||||
code TEXT NOT NULL UNIQUE,
|
||||
label TEXT NOT NULL,
|
||||
risk_weight NUMERIC(3,2) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS transactions (
|
||||
id UUID PRIMARY KEY,
|
||||
account_id UUID NOT NULL,
|
||||
kind TEXT NOT NULL,
|
||||
amount_minor BIGINT NOT NULL,
|
||||
currency CHAR(3),
|
||||
merchant_name TEXT,
|
||||
category_id SMALLINT,
|
||||
status TEXT NOT NULL,
|
||||
risk_score NUMERIC(4,3),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Parallel workers otherwise let a plain sequential scan of this table
|
||||
-- compete with an index scan on raw CPU parallelism for higher-selectivity
|
||||
-- accounts (e.g. the 5 hot ones, each ~6-8% of all rows) -- masking exactly
|
||||
-- the missing-index pathology this table's index policy (README §5) exists
|
||||
-- to demonstrate. Scoped to this one table, not a database-wide setting.
|
||||
ALTER TABLE transactions SET (parallel_workers = 0);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sanctions_entries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
normalized_name TEXT NOT NULL,
|
||||
country CHAR(2),
|
||||
list_source TEXT,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id UUID PRIMARY KEY,
|
||||
transaction_id UUID NOT NULL,
|
||||
channel TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
attempts INT NOT NULL DEFAULT 0,
|
||||
sent_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
transaction_id UUID NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
payload JSONB,
|
||||
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Not part of the base spec's domain model: a single-row table the seed
|
||||
-- script writes its summary manifest into, so both `scripts/smoke.py` and
|
||||
-- the test suite can read back what a given seed run actually produced
|
||||
-- (row counts, hot/sanctioned/poison account ids, the SEED value used)
|
||||
-- without re-deriving it via ad-hoc queries.
|
||||
CREATE TABLE IF NOT EXISTS seed_manifest (
|
||||
id SMALLINT PRIMARY KEY DEFAULT 1,
|
||||
data JSONB NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS accounts_holder_name_idx ON accounts (holder_name);
|
||||
CREATE INDEX IF NOT EXISTS sanctions_entries_normalized_name_idx ON sanctions_entries (normalized_name);
|
||||
Reference in New Issue
Block a user