alembic-migrations

Alembic Migration Patterns ()

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "alembic-migrations" with this command: npx skills add yonatangross/orchestkit/yonatangross-orchestkit-alembic-migrations

Alembic Migration Patterns ()

Database migration management with Alembic for SQLAlchemy 2.0 async applications.

Overview

  • Creating or modifying database tables and columns

  • Auto-generating migrations from SQLAlchemy models

  • Implementing zero-downtime schema changes

  • Rolling back or managing migration history

  • Adding indexes on large production tables

  • Setting up Alembic with async PostgreSQL (asyncpg)

Quick Reference

Initialize Alembic (Async Template)

Initialize with async template for asyncpg

alembic init -t async migrations

Creates:

- alembic.ini

- migrations/env.py (async-ready)

- migrations/script.py.mako

- migrations/versions/

Async env.py Configuration

migrations/env.py

import asyncio from logging.config import fileConfig

from sqlalchemy import pool from sqlalchemy.engine import Connection from sqlalchemy.ext.asyncio import async_engine_from_config

from alembic import context

Import your models' Base for autogenerate

from app.models.base import Base

config = context.config if config.config_file_name is not None: fileConfig(config.config_file_name)

target_metadata = Base.metadata

def run_migrations_offline() -> None: """Run migrations in 'offline' mode - generates SQL.""" url = config.get_main_option("sqlalchemy.url") context.configure( url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, ) with context.begin_transaction(): context.run_migrations()

def do_run_migrations(connection: Connection) -> None: context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): context.run_migrations()

async def run_async_migrations() -> None: """Run migrations in 'online' mode with async engine.""" connectable = async_engine_from_config( config.get_section(config.config_ini_section, {}), prefix="sqlalchemy.", poolclass=pool.NullPool, )

async with connectable.connect() as connection:
    await connection.run_sync(do_run_migrations)

await connectable.dispose()

def run_migrations_online() -> None: """Entry point for online migrations.""" asyncio.run(run_async_migrations())

if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online()

Migration Template

"""Add users table.

Revision ID: abc123 Revises: None Create Date: -01-17 10:00:00.000000 """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import UUID

revision = 'abc123' down_revision = None branch_labels = None depends_on = None

def upgrade() -> None: op.create_table( 'users', sa.Column('id', UUID(as_uuid=True), primary_key=True), sa.Column('email', sa.String(255), nullable=False), sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.func.now()), ) op.create_index('idx_users_email', 'users', ['email'], unique=True)

def downgrade() -> None: op.drop_index('idx_users_email', table_name='users') op.drop_table('users')

Autogenerate Migration

Generate from model changes

alembic revision --autogenerate -m "add user preferences"

Apply migrations

alembic upgrade head

Rollback one step

alembic downgrade -1

Generate SQL for review (production)

alembic upgrade head --sql > migration.sql

Check current revision

alembic current

Show migration history

alembic history --verbose

Running Async Code in Migrations

"""Migration with async operation.

NOTE: Alembic upgrade/downgrade cannot be async, but you can run async code using sqlalchemy.util.await_only workaround. """ from alembic import op from sqlalchemy import text from sqlalchemy.util import await_only

def upgrade() -> None: # Get connection (works with async dialect) connection = op.get_bind()

# For async-only operations, use await_only
# This works because Alembic runs in greenlet context
result = await_only(
    connection.execute(text("SELECT count(*) FROM users"))
)

# Standard operations work normally with async engine
op.execute("""
    CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_org
    ON users (organization_id, created_at DESC)
""")

Concurrent Index (Zero-Downtime)

def upgrade() -> None: # CONCURRENTLY avoids table locks on large tables # IMPORTANT: Cannot run inside transaction block op.execute(""" CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_org ON users (organization_id, created_at DESC) """)

def downgrade() -> None: op.execute("DROP INDEX CONCURRENTLY IF EXISTS idx_users_org")

In alembic.ini or env.py, disable transaction for this migration:

Set transaction_per_migration = false for CONCURRENTLY operations

Two-Phase NOT NULL Migration

"""Add org_id column (phase 1 - nullable).

Phase 1: Add nullable column Phase 2: Backfill data Phase 3: Add NOT NULL (separate migration after verification) """

def upgrade() -> None: # Phase 1: Add as nullable first op.add_column('users', sa.Column('org_id', UUID(as_uuid=True), nullable=True))

# Phase 2: Backfill with default org
op.execute("""
    UPDATE users
    SET org_id = 'default-org-uuid'
    WHERE org_id IS NULL
""")

# Phase 3 in SEPARATE migration after app updated:
# op.alter_column('users', 'org_id', nullable=False)

def downgrade() -> None: op.drop_column('users', 'org_id')

Key Decisions

Decision Recommendation Rationale

Async dialect Use postgresql+asyncpg

Native async support

NOT NULL column Two-phase: nullable first, then alter Avoids locking, backward compatible

Large table index CREATE INDEX CONCURRENTLY

Zero-downtime, no table locks

Column rename 4-phase expand/contract Safe migration without downtime

Autogenerate review Always review generated SQL May miss custom constraints

Migration granularity One logical change per file Easier rollback and debugging

Production deployment Generate SQL, review, then apply Never auto-run in production

Downgrade function Always implement properly Ensures reversibility

Transaction mode Default on, disable for CONCURRENTLY CONCURRENTLY requires no transaction

Anti-Patterns (FORBIDDEN)

NEVER: Add NOT NULL without default or two-phase approach

op.add_column('users', sa.Column('org_id', UUID, nullable=False)) # LOCKS TABLE, FAILS!

NEVER: Use blocking index creation on large tables

op.create_index('idx_large', 'big_table', ['col']) # LOCKS TABLE - use CONCURRENTLY

NEVER: Skip downgrade implementation

def downgrade(): pass # WRONG - implement proper rollback

NEVER: Modify migration after deployment

Create a new migration instead!

NEVER: Run migrations automatically in production

Use: alembic upgrade head --sql > review.sql

NEVER: Use asyncio.run() in env.py if loop exists

Already handled by async template, but check for FastAPI lifespan conflicts

NEVER: Run CONCURRENTLY inside transaction

op.execute("BEGIN; CREATE INDEX CONCURRENTLY ...; COMMIT;") # FAILS

Alembic with FastAPI Lifespan

When running migrations during FastAPI startup (advanced)

Issue: Event loop already running

Solution 1: Run migrations before app starts (recommended)

In entrypoint.sh:

alembic upgrade head && uvicorn app.main:app

Solution 2: Use run_sync for programmatic migrations

from sqlalchemy import Connection from alembic import command from alembic.config import Config

async def run_migrations(connection: Connection) -> None: """Run migrations programmatically within existing async context.""" def do_upgrade(connection: Connection): config = Config("alembic.ini") config.attributes["connection"] = connection command.upgrade(config, "head")

await connection.run_sync(do_upgrade)

Related Skills

  • database-schema-designer

  • Schema design and normalization patterns

  • database-versioning

  • Version control and change management

  • zero-downtime-migration

  • Expand/contract patterns for safe migrations

  • sqlalchemy-2-async

  • Async SQLAlchemy session patterns

  • integration-testing

  • Testing migrations with test databases

Capability Details

autogenerate-migrations

Keywords: autogenerate, auto-generate, revision, model sync, compare Solves:

  • Auto-generate migrations from SQLAlchemy models

  • Sync database with model changes

  • Detect schema drift

revision-management

Keywords: upgrade, downgrade, rollback, history, current, revision Solves:

  • Apply or rollback migrations

  • View migration history

  • Check current database version

zero-downtime-changes

Keywords: concurrent, expand contract, online migration, no downtime Solves:

  • Add indexes without locking

  • Rename columns safely

  • Large table migrations

data-migration

Keywords: backfill, data migration, transform, batch update Solves:

  • Backfill new columns with data

  • Transform existing data

  • Migrate between column formats

async-configuration

Keywords: asyncpg, async engine, env.py async, run_async_migrations Solves:

  • Configure Alembic for async SQLAlchemy

  • Run migrations with asyncpg

  • Handle existing event loop conflicts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

responsive-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

domain-driven-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

dashboard-patterns

No summary provided by upstream source.

Repository SourceNeeds Review