mitai-jinkendo/.claude/docs/technical/MIGRATIONS.md
Lars 7940dc7560 docs: Struktur .claude/docs versionieren, working/, Gitea-Index, Regeln
- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren
- DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues
- .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08)
- Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben
- docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md
- CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht

Made-with: Cursor
2026-04-08 13:01:49 +02:00

393 lines
11 KiB
Markdown

# 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