Skip to content
411sstPublic

About

PostgreSQL schema-migration risk analyzer — parses DDL/ORM changes through pglast into a dependency graph, classifies impact via HistGradientBoosting + Bayesian posterior + LLM verdict, deployed with rollback SQL generation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Domino — Schema Migration Impact Analyzer

Domino takes a proposed database schema migration and traces every downstream query it would break or make silently return wrong results, models the second-order effects across applications and schedules, predicts migration risk from historical patterns with a hybrid Bayesian + LLM model, and generates a safe execution plan — all orchestrated by an agentic pipeline that investigates hypotheses and backtracks when they don't hold.

Give it a migration like ALTER TABLE orders ALTER COLUMN total TYPE numeric(12,4) and it will tell you that your billing service's financial SUM(total) will silently start carrying two extra decimals, that the change rewrites a 2.5M-row table under an ACCESS EXCLUSIVE lock, and how to roll it all back.


What makes it interesting

Three things carry the project:

  1. PostgreSQL depth. The metadata store is not a dumb key-value bucket. It uses tstzrange + a btree_gist exclusion constraint for temporal schema versioning, recursive CTEs to traverse the impact DAG and detect cycles, GIN indexes on JSONB for queryable operation/predicate bags, window functions (LAG) for migration-cadence features, and materialized views for the query↔schema dependency map. See docs/schema_reference.md (generated by scripts/export_schema_docs.py).

  2. Agentic reasoning. A LangGraph state machine parses the migration, identifies affected objects, classifies impact, traces second-order effects, predicts risk, generates a plan, and validates + backtracks on conflicts. It is model-agnostic (default Groq/LLaMA) and degrades cleanly to deterministic-only analysis when no LLM is configured or reachable.

  3. The Bayesian learning loop. A Dirichlet-multinomial severity model starts from domain base rates (clean 82% · minor 9% · major 5% · rollback 3% · outage 1%) and updates exactly (conjugacy) as you record real outcomes — getting more accurate and more personalized to your codebase over time.


The seven impact categories

Every affected query is classified into exactly one of:

Category Meaning
Breaking Query will error out after the migration.
Silent change Runs, but returns different results immediately.
Temporal drift Results diverge slowly over time (e.g. a default changed).
Performance degradation Runs slower (dropped index, table rewrite, new scan).
Locking risk Blocked during the migration's execution window.
Partial break Fails for specific input values only (e.g. a new CHECK).
Safe Unaffected.

Silent-change detection covers six sub-types (type widening/narrowing, nullability, default changes, column rename, added-column-affecting-aggregation, constraint changes) with documented sensitivity thresholds.


Tech stack

Backend — Python 3.12+, FastAPI, psycopg3 (async for the API, sync for scripts), LangGraph + langchain-core, PyMC + ArviZ, scikit-learn + SHAP, pglast, Python ast, Groq SDK (default LLM, swappable).

Frontend — React + Vite + Tailwind, React Flow (impact graph), html2pdf.js (client-side PDF export).

Infra — Docker Compose (PostgreSQL 16); local-first.


Architecture

Migration (raw SQL / .sql / Alembic / snapshot diff)
        │  ingestion: pglast + regex fallback + ast query extraction
        ▼
Parsed operations ── query corpus (full predicate tracking) ── schema snapshot
        │  analysis: 7-category detectors + dependency DAG + planner
        ▼
LangGraph agent ── LLM (optional) ─┐
        │                          │  three-layer risk:
        ▼                          │   1. sklearn + SHAP  (score + why)
Impact result + DAG + plan ────────┤   2. LLM severity     (verdict)
        │                          │   3. Bayesian          (distribution)
        ▼                          ┘
React UI  ◀── async polling (4 grouped stages)

Feature-grouped layout:

backend/
  db/            schema.sql, connection (async pool + sync), seed
  targets/       repo + database connection pairs
  ingestion/     migration_parser, query_extractor, schema_snapshot, repo_connector, routes
  analysis/      query_parser, 7 detectors, dependency_graph, migration_planner, pipeline, routes
  agent/         state, llm (model-agnostic), nodes/, tools/, graph (LangGraph)
  ml/            features, explainability (sklearn+SHAP), bayesian_model (PyMC+ArviZ), routes
frontend/        React + Vite + Tailwind
scripts/         demo_setup, seed_schema, train_model, collect_training_data, reset_db, ...
tests/           pytest (parsers, detectors, agent flow, risk model)

Running it

1. Database

With Docker (recommended):

docker compose up -d          # PostgreSQL 16 on localhost:5432 (user/pass/db = domino)

If port 5432 is already in use, set DB_PORT in .env to a free port first.

Without Docker: point DATABASE_URL at any PostgreSQL 14+ instance where the role can CREATE EXTENSION btree_gist (i.e. a superuser, or a DB where the extension is pre-created).

2. Backend

python -m venv .venv
.venv/Scripts/activate            # Windows;  source .venv/bin/activate on macOS/Linux
pip install -r requirements.txt

cp .env.example .env              # then edit if needed
python scripts/seed_schema.py     # create the schema (22 tables + functions + views)
python scripts/health_check.py    # verify everything works

python -m backend                 # start the API on http://localhost:8000

Windows note: start the API with python -m backend (not uvicorn backend.main:app). psycopg3's async pool needs a SelectorEventLoop, and the launcher installs it before uvicorn creates its loop. On macOS/Linux uvicorn backend.main:app --reload works directly.

3. LLM (optional)

The agent runs deterministic-only with no key. For LLM reasoning, set GROQ_API_KEY (or switch LLM_PROVIDER/LLM_MODEL to OpenAI/Anthropic — the agent is model-agnostic and needs no code changes).

4. Risk model (optional but recommended)

python scripts/train_model.py     # trains on synthetic data -> backend/ml/data/risk_model.joblib

Without an artifact the risk layer falls back to a transparent heuristic with the identical SHAP-style interface. scripts/collect_training_data.py scrapes real Django/Rails migrations from GitHub (needs GITHUB_TOKEN) to augment training.

5. Frontend

cd frontend
npm install
npm run dev                       # http://localhost:5173  (proxies /api to :8000)

The demo

python scripts/demo_setup.py --reset

This creates a separate domino_demo e-commerce database (users, orders, line_items, products), registers it as a target, captures a baseline snapshot, and adds two applications with sample queries: billing-service (critical) and admin-dashboard (medium).

Open the frontend, make sure the E-Commerce (demo) target is selected, and analyze:

ALTER TABLE orders ALTER COLUMN total TYPE numeric(12,4);   -- was numeric(10,2)

Expected result: the billing service's SUM(total) and order-total queries light up as silent changes (numeric scale increased 2→4 → arithmetic and equality comparisons differ), the admin count query shows performance/locking risk from the full-table rewrite on the ~2.5M-row orders table, the risk score lands in the HIGH band with a SHAP breakdown (app criticality + table rewrite drive it up), and the execution plan recommends a maintenance window with a generated rollback script.


API

/api/targets/…    manage repo + database connection pairs
/api/ingest/…     migrations (4 input paths), snapshots, repo, query corpus
/api/analyze/     POST to submit (returns session_id), GET /{id} to poll
/api/history/…    past analyses
/api/ml/…         risk readback, Bayesian distribution, outcome recording, git-monitor

Analysis is async: POST /api/analyze/ returns a session_id immediately, then GET /api/analyze/{session_id} polls through four grouped stages — Parsing migration → Analyzing queries → Tracing downstream impact → Calculating risk → Done/Failed.


Error handling

  • LLM down/rate-limited → graceful degradation to deterministic-only analysis, flagged in the results.
  • Can't reach the target database → fails hard with a clear error (stale schema would produce misleading analysis).
  • pglast can't parse the migration → falls back to regex extraction of table/column names; partial results are flagged lower-confidence.

Testing

python -m pytest            # parsers, all 7 detectors, dependency graph, agent flow, risk model

The suite runs anywhere; DB-backed tests skip cleanly when no database is configured (set DATABASE_URL to include them).


Recording outcomes (the learning loop)

After you actually run a migration, record what happened — manually via POST /api/ml/outcomes, or let POST /api/ml/git-monitor scan the connected repo for fix/revert/hotfix/rollback commits within 48 hours and flag them for confirmation. Each confirmed outcome updates the target's Bayesian posterior, so Domino's severity estimates get sharper and more specific to your codebase.


Status / deployment

Local-first. Auth and encrypted credential storage are intentionally deferred; add an API key layer and encrypt the targets.db_password column before any multi-tenant deployment (credentials are plaintext for local dev today).

About

PostgreSQL schema-migration risk analyzer — parses DDL/ORM changes through pglast into a dependency graph, classifies impact via HistGradientBoosting + Bayesian posterior + LLM verdict, deployed with rollback SQL generation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages