# Database Migrations System **Version:** v9c **Implementiert:** 2026-03-21 **Dokumentiert:** 2026-03-21 --- ## Übersicht Mitai Jinkendo verwendet ein automatisches Migrations-System für strukturierte Schema-Änderungen. Alle Migrationen werden beim Container-Start automatisch ausgeführt. ## Architektur ### Migration-Tracking **Tabelle:** `schema_migrations` ```sql CREATE TABLE schema_migrations ( id SERIAL PRIMARY KEY, filename VARCHAR(255) UNIQUE NOT NULL, applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ) ``` Diese Tabelle trackt, welche Migrationen bereits angewendet wurden. ### Ablauf beim Container-Start 1. **PostgreSQL Connection Check** (`db_init.py`) 2. **Schema-Initialisierung** (falls `profiles` Tabelle nicht existiert) - Lädt `backend/schema.sql` (Basis-Schema) 3. **Migrations-System** (`run_migrations()`) - Erstellt `schema_migrations` Tabelle (falls nicht vorhanden) - Scannt `backend/migrations/` nach `.sql` Dateien - Filtert nur nummerierte Dateien: `\d{3}_*.sql` (z.B. `001_feature.sql`) - Sortiert alphabetisch (aufsteigende Reihenfolge) - Wendet nur noch nicht angewendete Migrationen an - Trackt jede erfolgreich angewendete Migration 4. **SQLite-zu-PostgreSQL Migration** (falls vorhanden) ## Datei-Konventionen ### Naming-Pattern **Format:** `XXX_descriptive_name.sql` - `XXX` = Dreistellige Nummer (001, 002, 003, ...) - Unterstrich `_` als Trennzeichen - Kleinbuchstaben, keine Leerzeichen - `.sql` Extension **Beispiele:** ``` ✅ 001_subscription_system.sql ✅ 002_fix_features.sql ✅ 003_add_email_verification.sql ❌ v9c_subscription_system.sql # Keine Nummer ❌ check_features.sql # Keine Nummer ❌ 1_feature.sql # Nur eine Ziffer ❌ 001-feature.sql # Bindestrich statt Unterstrich ``` ### Datei-Struktur Jede Migration sollte folgende Struktur haben: ```sql -- ================================================================ -- Migration XXX: Beschreibung -- Version: vX.X -- Date: YYYY-MM-DD -- ================================================================ -- Beschreibung der Änderung ALTER TABLE table_name ... -- Weitere SQL-Statements -- Kommentare für Dokumentation COMMENT ON COLUMN table.column IS 'Beschreibung'; ``` ### SQL-Einschränkungen **Erlaubt:** - Standard SQL DDL (CREATE, ALTER, DROP) - Standard SQL DML (INSERT, UPDATE, DELETE) - CREATE INDEX, CREATE FUNCTION, etc. - Multi-Statement-Scripts (durch `;` getrennt) **Nicht erlaubt:** - psql Meta-Kommandos (`\echo`, `\set`, `\connect`, etc.) - Interactive Commands (`\i`, `\include`) - Transaktions-Kontrolle (automatisch gehandhabt) ## Anwendung ### Automatisch (Production/Dev) Migrationen werden **automatisch** beim Container-Start angewendet: ```bash # Container startet docker compose up -d # db_init.py wird ausgeführt: # 1. PostgreSQL ready check # 2. Schema initialisiert (falls nötig) # 3. Migrationen ausgeführt # 4. SQLite-Migration (falls nötig) ``` **Log-Output:** ``` ═══════════════════════════════════════════════════════════ MITAI JINKENDO - Database Initialization (v9c) ═══════════════════════════════════════════════════════════ Checking PostgreSQL connection... ✓ PostgreSQL ready Checking database schema... ✓ Schema already exists Running database migrations... Found 2 pending migration(s)... ✓ Applied: 001_subscription_system.sql ✓ Applied: 003_add_email_verification.sql ✓ Database initialization complete ``` ### Manuell (Entwicklung) Für Testing während der Entwicklung: ```bash # Im laufenden Container docker exec -it dev-mitai-api python3 /app/db_init.py # Oder direkt mit psql (für einzelne Migrationen) docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \ -f /path/to/migration.sql ``` ## Migration erstellen ### Schritt 1: Datei erstellen ```bash # Nächste freie Nummer ermitteln ls backend/migrations/ | grep -E '^\d{3}_' | sort | tail -1 # → 003_add_email_verification.sql # Neue Migration mit Nummer 004 touch backend/migrations/004_add_new_feature.sql ``` ### Schritt 2: SQL schreiben ```sql -- ================================================================ -- Migration 004: Add New Feature -- Version: v9d -- Date: 2026-03-22 -- ================================================================ -- Add new column ALTER TABLE profiles ADD COLUMN IF NOT EXISTS new_feature_enabled BOOLEAN DEFAULT TRUE; -- Create index if needed CREATE INDEX IF NOT EXISTS idx_profiles_new_feature ON profiles(new_feature_enabled) WHERE new_feature_enabled = TRUE; -- Update existing data if needed UPDATE profiles SET new_feature_enabled = TRUE WHERE tier = 'premium'; COMMENT ON COLUMN profiles.new_feature_enabled IS 'Feature flag for new feature'; ``` ### Schritt 3: Testen ```bash # Lokal testen (Dev-Container) git add backend/migrations/004_add_new_feature.sql git commit -m "feat: add migration for new feature" git push origin develop # Container wird neu gebaut und Migration automatisch angewendet ``` ### Schritt 4: Verifizieren ```bash # Prüfen ob Migration angewendet wurde docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \ -c "SELECT * FROM schema_migrations ORDER BY applied_at DESC LIMIT 5;" # Output: # id | filename | applied_at # ---+------------------------------+------------------------ # 4 | 004_add_new_feature.sql | 2026-03-22 10:30:15+00 # 3 | 003_add_email_verification.sql| 2026-03-21 15:20:10+00 # 2 | 002_fix_features.sql | 2026-03-20 12:10:05+00 # 1 | 001_subscription_system.sql | 2026-03-20 12:10:00+00 ``` ## Rollback-Strategie **Automatischer Rollback:** Nicht implementiert **Manueller Rollback:** 1. **Identifizieren der problematischen Migration:** ```bash docker logs dev-mitai-api | grep "Failed to apply" ``` 2. **Migration aus Tracking entfernen:** ```sql DELETE FROM schema_migrations WHERE filename = '004_broken_migration.sql'; ``` 3. **Änderungen manuell rückgängig machen:** ```sql -- Beispiel: Spalte entfernen ALTER TABLE profiles DROP COLUMN new_feature_enabled; ``` 4. **Migration-Datei korrigieren** ```bash vim backend/migrations/004_broken_migration.sql ``` 5. **Container neu starten** (Migration wird erneut ausgeführt) ```bash docker compose restart api ``` ## Best Practices ### ✅ DO - **Immer `IF NOT EXISTS` / `IF EXISTS` verwenden:** ```sql ALTER TABLE profiles ADD COLUMN IF NOT EXISTS email_verified BOOLEAN; CREATE INDEX IF NOT EXISTS idx_profiles_email ON profiles(email); ``` - **Idempotente Migrationen schreiben:** Migration kann mehrfach ausgeführt werden ohne Fehler - **Daten-Migrationen mit Bedacht:** ```sql UPDATE profiles SET email_verified = TRUE WHERE email IS NOT NULL AND email_verified IS NULL; ``` - **Kommentare für Dokumentation:** ```sql COMMENT ON COLUMN profiles.email_verified IS 'Whether email has been verified'; ``` - **Indices für Performance:** ```sql CREATE INDEX IF NOT EXISTS idx_profiles_verification_token ON profiles(verification_token) WHERE verification_token IS NOT NULL; -- Partial index ``` ### ❌ DON'T - **Keine psql Meta-Kommandos:** ```sql \echo "Starting migration" -- ❌ Funktioniert nicht SELECT 'Starting migration'; -- ✅ Funktioniert ``` - **Keine Breaking Changes ohne Staging:** ```sql ALTER TABLE profiles DROP COLUMN tier; -- ❌ App wird brechen ``` - **Keine Hardcoded Values für produktive Daten:** ```sql INSERT INTO profiles (id, email, ...) VALUES (1, 'admin@example.com', ...); -- ❌ ``` - **Keine Foreign Keys ohne Fallback:** ```sql -- ❌ Ohne ON DELETE Behavior ALTER TABLE subscriptions ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id) REFERENCES profiles(id); -- ✅ Mit Cascade ALTER TABLE subscriptions ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE; ``` ## Troubleshooting ### Migration schlägt fehl **Symptom:** Container startet nicht, Logs zeigen `✗ Failed to apply XXX.sql` **Lösung:** 1. Logs prüfen: `docker logs dev-mitai-api | tail -50` 2. Fehlerhafte Migration identifizieren 3. Aus Tracking entfernen + Schema manuell korrigieren 4. Migration-Datei fixen 5. Container neu starten ### Migration wurde nicht angewendet **Symptom:** Neue Migration-Datei wird ignoriert **Ursachen:** - Datei entspricht nicht dem Pattern `\d{3}_*.sql` - Datei wurde bereits in `schema_migrations` getrackt - Datei ist nicht im Container (`backend/migrations/` Volume gemountet?) **Lösung:** ```bash # Prüfen ob Datei gemountet ist docker exec -it dev-mitai-api ls -la /app/migrations/ # Prüfen ob bereits getrackt docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \ -c "SELECT * FROM schema_migrations WHERE filename = '004_feature.sql';" # Falls fälschlich getrackt, entfernen docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \ -c "DELETE FROM schema_migrations WHERE filename = '004_feature.sql';" # Container neu starten docker compose restart api ``` ### Datenbank-Schema inkonsistent **Symptom:** `schema_migrations` zeigt Migration als angewendet, aber Änderungen fehlen **Ursachen:** - Migration wurde manuell ausgeführt, aber Tracking war kaputt - Datenbank wurde zurückgesetzt, aber Tracking-Tabelle nicht **Lösung:** ```bash # Schema neu aufbauen (ACHTUNG: Datenverlust!) docker compose down -v # Löscht Volumes docker compose up -d # Baut alles neu auf # ODER: Tracking-Tabelle manuell korrigieren docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \ -c "TRUNCATE schema_migrations; -- Alle Tracking-Einträge löschen" # Container neu starten → Alle Migrationen werden erneut angewendet docker compose restart api ``` ## Migrations-Historie | Nr. | Datei | Version | Datum | Beschreibung | |-----|-------|---------|-------|--------------| | 003 | `003_add_email_verification.sql` | v9c | 2026-03-21 | Email-Verifizierung (verification_token, email_verified, verification_expires) | | 002 | `002_fix_features.sql` (manuell) | v9c | 2026-03-20 | Feature-System Fixes | | 001 | `001_subscription_system.sql` (manuell) | v9c | 2026-03-20 | Membership-System (tiers, subscriptions, coupons, access_grants) | **Hinweis:** Migrationen 001 und 002 wurden vor Einführung des automatischen Systems manuell angewendet und sind nicht nummeriert. Ab Migration 003 läuft alles automatisch. --- ## Referenzen - **Code:** `backend/db_init.py` (Zeilen 94-200) - **Migrations-Ordner:** `backend/migrations/` - **Tracking-Tabelle:** `schema_migrations` - **Startup-Script:** `backend/startup.sh` (ruft `db_init.py` auf) - **Dokumentation:** `.claude/docs/technical/MIGRATIONS.md` (diese Datei) --- **Dokumentiert:** 2026-03-21 **Letzte Änderung:** 2026-03-21