feat: add Participant and MagicToken models with automatic migrations
Implements Phase 2 infrastructure for participant registration and authentication: Database Models: - Add Participant model with exchange scoping and soft deletes - Add MagicToken model for passwordless authentication - Add participants relationship to Exchange model - Include proper indexes and foreign key constraints Migration Infrastructure: - Generate Alembic migration for new models - Create entrypoint.sh script for automatic migrations on container startup - Update Containerfile to use entrypoint script and include uv binary - Remove db.create_all() in favor of migration-based schema management This establishes the foundation for implementing stories 4.1-4.3, 5.1-5.3, and 10.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
266
docs/designs/v0.2.0/MIGRATION-FLOW.md
Normal file
266
docs/designs/v0.2.0/MIGRATION-FLOW.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Database Migration Flow
|
||||
|
||||
## Container Startup Sequence
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ User runs: podman run sneaky-klaus │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Container starts entrypoint.sh │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Step 1: Run "uv run alembic upgrade head" │
|
||||
│ │
|
||||
│ Alembic checks /app/data/sneaky-klaus.db │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ Database exists │ │ No database │
|
||||
│ (update scenario) │ │ (first run) │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ Read alembic_version │ │ Create database file │
|
||||
│ table │ │ │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ Apply only new │ │ Run all migrations │
|
||||
│ migrations │ │ from scratch │
|
||||
│ (incremental) │ │ │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
│ │
|
||||
└───────────────┬───────────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Success │ │ Failure │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Log success │ │ Log error │
|
||||
│ message │ │ Exit code 1 │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ Step 2: Start │ │ Container stops │
|
||||
│ gunicorn │ │ (failed state) │
|
||||
│ │ │ │
|
||||
│ Application │ │ User checks logs │
|
||||
│ ready to serve │ │ to debug │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
## Migration Scenarios
|
||||
|
||||
### Scenario 1: Fresh Installation (First Run)
|
||||
|
||||
```
|
||||
User action: podman run sneaky-klaus:v0.2.0
|
||||
|
||||
Container startup:
|
||||
1. entrypoint.sh executes
|
||||
2. alembic upgrade head runs
|
||||
- No database file exists
|
||||
- Creates /app/data/sneaky-klaus.db
|
||||
- Creates alembic_version table
|
||||
- Runs migration eeff6e1a89cd (Admin, Exchange)
|
||||
- Runs migration abc123def456 (Participant, MagicToken)
|
||||
- Sets current version: abc123def456
|
||||
3. gunicorn starts
|
||||
4. Application ready
|
||||
|
||||
Result: Fresh database with all tables
|
||||
```
|
||||
|
||||
### Scenario 2: Update from v0.1.0 to v0.2.0
|
||||
|
||||
```
|
||||
User action:
|
||||
1. podman pull sneaky-klaus:v0.2.0
|
||||
2. podman stop sneaky-klaus
|
||||
3. podman rm sneaky-klaus
|
||||
4. podman run sneaky-klaus:v0.2.0 (same volume)
|
||||
|
||||
Container startup:
|
||||
1. entrypoint.sh executes
|
||||
2. alembic upgrade head runs
|
||||
- Database file exists
|
||||
- Reads alembic_version table
|
||||
- Current version: eeff6e1a89cd
|
||||
- Detects new migration: abc123def456
|
||||
- Runs migration abc123def456 (adds Participant, MagicToken)
|
||||
- Updates current version: abc123def456
|
||||
3. gunicorn starts
|
||||
4. Application ready
|
||||
|
||||
Result: Updated database with new tables, existing data preserved
|
||||
```
|
||||
|
||||
### Scenario 3: Already Up-to-Date
|
||||
|
||||
```
|
||||
User action: podman restart sneaky-klaus
|
||||
|
||||
Container startup:
|
||||
1. entrypoint.sh executes
|
||||
2. alembic upgrade head runs
|
||||
- Database file exists
|
||||
- Reads alembic_version table
|
||||
- Current version: abc123def456
|
||||
- No new migrations to apply
|
||||
- Logs "Already at head"
|
||||
3. gunicorn starts
|
||||
4. Application ready
|
||||
|
||||
Result: No changes, fast startup
|
||||
```
|
||||
|
||||
### Scenario 4: Migration Failure
|
||||
|
||||
```
|
||||
User action: podman run sneaky-klaus:v0.3.0 (hypothetical buggy migration)
|
||||
|
||||
Container startup:
|
||||
1. entrypoint.sh executes
|
||||
2. alembic upgrade head runs
|
||||
- Database file exists
|
||||
- Current version: abc123def456
|
||||
- Attempts new migration: xyz789bad000
|
||||
- Migration fails (SQL error, constraint violation, etc.)
|
||||
- Alembic rolls back transaction
|
||||
- Returns exit code 1
|
||||
3. entrypoint.sh detects failure
|
||||
- Logs error message
|
||||
- Exits with code 1
|
||||
4. Container stops (failed state)
|
||||
|
||||
Result: Database unchanged, container not running
|
||||
User action: Check logs, report bug, or fix database manually
|
||||
```
|
||||
|
||||
## Comparison: Manual vs Automatic Migrations
|
||||
|
||||
### Manual Migration Workflow (without this feature)
|
||||
|
||||
```
|
||||
User workflow:
|
||||
1. podman pull sneaky-klaus:v0.2.0
|
||||
2. podman stop sneaky-klaus
|
||||
3. podman exec sneaky-klaus bash ← Extra step
|
||||
4. uv run alembic upgrade head ← Manual command
|
||||
5. exit ← Extra step
|
||||
6. podman start sneaky-klaus
|
||||
|
||||
Problems:
|
||||
- Requires command-line knowledge
|
||||
- Easy to forget
|
||||
- Error-prone
|
||||
- Not friendly for non-technical users
|
||||
```
|
||||
|
||||
### Automatic Migration Workflow (with this feature)
|
||||
|
||||
```
|
||||
User workflow:
|
||||
1. podman pull sneaky-klaus:v0.2.0
|
||||
2. podman stop sneaky-klaus
|
||||
3. podman rm sneaky-klaus
|
||||
4. podman run sneaky-klaus:v0.2.0
|
||||
|
||||
Benefits:
|
||||
- Simple, standard container workflow
|
||||
- Cannot forget to run migrations
|
||||
- Migrations guaranteed to run before app starts
|
||||
- Self-hosted friendly
|
||||
```
|
||||
|
||||
## File Structure After Implementation
|
||||
|
||||
```
|
||||
sneaky-klaus/
|
||||
├── entrypoint.sh ← NEW: Migration + startup script
|
||||
├── Containerfile ← MODIFIED: Use entrypoint
|
||||
├── src/
|
||||
│ └── app.py ← MODIFIED: Remove db.create_all()
|
||||
├── migrations/
|
||||
│ ├── env.py ← (existing)
|
||||
│ └── versions/
|
||||
│ ├── eeff6e1a89cd_....py ← (existing)
|
||||
│ └── abc123def456_....py ← NEW: Phase 2 migration
|
||||
└── alembic.ini ← (existing)
|
||||
```
|
||||
|
||||
## Developer vs Production Workflows
|
||||
|
||||
### Developer Workflow (Local Development)
|
||||
|
||||
```bash
|
||||
# Developer makes schema change
|
||||
1. Edit src/models/participant.py
|
||||
2. uv run alembic revision --autogenerate -m "Add Participant model"
|
||||
3. Review generated migration file
|
||||
4. uv run alembic upgrade head ← Manual migration
|
||||
5. uv run pytest ← Test
|
||||
6. git add migrations/versions/...
|
||||
7. git commit -m "feat: add Participant model"
|
||||
```
|
||||
|
||||
Developers retain explicit control over when migrations run.
|
||||
|
||||
### Production Workflow (Container Deployment)
|
||||
|
||||
```bash
|
||||
# Self-hosted user updates to new version
|
||||
1. podman pull sneaky-klaus:v0.2.0
|
||||
2. podman-compose down
|
||||
3. podman-compose up -d ← Migrations run automatically
|
||||
|
||||
# No manual migration step needed
|
||||
```
|
||||
|
||||
Users get automatic, safe migrations without extra commands.
|
||||
|
||||
## Security & Safety Considerations
|
||||
|
||||
### Why This is Safe
|
||||
|
||||
1. **Atomic migrations**: Alembic uses transactions (rollback on failure)
|
||||
2. **Version tracking**: alembic_version table prevents re-running migrations
|
||||
3. **Fail-safe**: Container won't start if migration fails
|
||||
4. **No data loss**: Migrations are additive (add tables/columns)
|
||||
5. **Tested**: All migrations tested before release
|
||||
|
||||
### What Could Go Wrong
|
||||
|
||||
1. **Migration bug**: Bad migration could fail or corrupt data
|
||||
- **Mitigation**: Thorough testing of migrations before release
|
||||
- **Recovery**: Database backup (future enhancement)
|
||||
|
||||
2. **Permission issue**: Container can't write to database file
|
||||
- **Mitigation**: Volume permissions documentation
|
||||
- **Recovery**: Fix volume permissions and restart
|
||||
|
||||
3. **Disk full**: No space for database changes
|
||||
- **Mitigation**: Health checks and monitoring
|
||||
- **Recovery**: Free up disk space and restart
|
||||
|
||||
## References
|
||||
|
||||
- ADR-0005: Database Migrations with Alembic
|
||||
- Automatic Migration Implementation Guide
|
||||
- Phase 2 Implementation Decisions (Section 9.2)
|
||||
Reference in New Issue
Block a user