Skip to content

Replace create_all() with Alembic migrations#15

Merged
vladiant merged 4 commits into
mainfrom
add_alembic_migrations
Apr 3, 2026
Merged

Replace create_all() with Alembic migrations#15
vladiant merged 4 commits into
mainfrom
add_alembic_migrations

Conversation

@vladiant
Copy link
Copy Markdown
Collaborator

@vladiant vladiant commented Apr 3, 2026

Summary

Replaces the SQLAlchemy Base.metadata.create_all() startup call with proper Alembic schema migrations, enabling safe, incremental, and reversible database schema changes in production.

Motivation

The previous approach used create_all() during FastAPI lifespan startup to create tables. While convenient for development, this is unsuitable for production:

  • Cannot track or apply incremental schema changes
  • No rollback capability
  • No migration history
  • Race condition when Uvicorn runs multiple workers (each worker called create_all() concurrently)

Changes

New files:

  • alembic.ini — Alembic configuration (DB URL resolved at runtime via Settings)
  • env.py — Async migration environment using asyncpg
  • 0001_initial_schema.py — Idempotent initial migration for the images table (14 columns, 3 indexes)
  • entrypoint.sh — Docker entrypoint that runs alembic upgrade head once before Uvicorn forks workers

Modified files:

  • main.py — Removed create_all() from lifespan; migrations now run via Docker entrypoint
  • Dockerfile — Copies alembic.ini, migrations, and entrypoint.sh; sets ENTRYPOINT
  • test_lifespan.py — Simplified; removed PG type-swapping helpers and Alembic mocking
  • CHANGELOG.md — Added [2.2.1] entry
  • pyproject.toml — Version bump 2.2.02.2.1
  • README.md — Added migrations to project structure and a "Database Migrations" section
  • PROJECT_DESCRIPTION.md — Added migration-related bullets

Design decisions

Decision Rationale
Entrypoint-based migration Avoids multi-worker race condition — migrations run once before Uvicorn forks
Idempotent initial migration Handles pre-existing tables from create_all() deployments by checking inspector.get_table_names()
No hardcoded DB URL in alembic.ini env.py reads credentials from Settings (pydantic-settings) at runtime
PATCH version bump No new API endpoints or breaking changes — internal infrastructure improvement

Testing

  • All 216 tests pass with 87.80% coverage
  • Verified on minikube — all 12 demo steps pass, pod stable with 0 restarts
  • Lint (ruff) and type-check (mypy) clean

How to use

# Auto-applied in Docker via entrypoint
docker compose up --build

# Manual migration commands
alembic upgrade head              # Apply all migrations
alembic downgrade -1              # Roll back one migration
alembic revision --autogenerate -m "description"  # Generate new migration

Diff stats

13 files changed, 471 insertions(+), 111 deletions(-)

@vladiant vladiant merged commit 7da5bda into main Apr 3, 2026
4 checks passed
@vladiant vladiant deleted the add_alembic_migrations branch April 3, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant