Spaces:
Sleeping
Sleeping
| """ | |
| Migration runner used at container start. | |
| Three situations have to be handled, because this project ran | |
| Base.metadata.create_all() at startup for a long time and never ran Alembic: | |
| 1. Fresh database, nothing in it | |
| -> upgrade to head normally. | |
| 2. Existing database created by create_all(), no alembic_version table | |
| -> the tables already exist, so running the baseline revision would | |
| fail with "relation already exists". Adopt the schema by stamping | |
| it at the baseline, then apply anything newer. | |
| 3. Database already under Alembic control | |
| -> upgrade to head normally. | |
| Run directly: python -m scripts.migrate | |
| """ | |
| import logging | |
| import sys | |
| from sqlalchemy import inspect | |
| from alembic import command | |
| from alembic.config import Config | |
| from app.db.session import engine, ensure_indexes | |
| logging.basicConfig( | |
| level=logging.INFO, format="%(asctime)s - migrate - %(levelname)s - %(message)s" | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # The revision whose schema matches what create_all() produced. | |
| BASELINE_REVISION = "001" | |
| def _alembic_config() -> Config: | |
| cfg = Config("alembic.ini") | |
| cfg.set_main_option("sqlalchemy.url", str(engine.url)) | |
| return cfg | |
| def run() -> None: | |
| inspector = inspect(engine) | |
| tables = set(inspector.get_table_names()) | |
| cfg = _alembic_config() | |
| if "alembic_version" in tables: | |
| logger.info("Database is under Alembic control — upgrading to head") | |
| command.upgrade(cfg, "head") | |
| elif "documents" in tables: | |
| # Case 2: pre-Alembic schema built by create_all(). | |
| logger.warning( | |
| "Found an existing schema with no alembic_version table. " | |
| "Adopting it at revision %s, then applying newer revisions.", | |
| BASELINE_REVISION, | |
| ) | |
| command.stamp(cfg, BASELINE_REVISION) | |
| command.upgrade(cfg, "head") | |
| # Stamping marks the baseline as applied without running it, so the | |
| # indexes that revision creates are skipped. create_all() never made | |
| # them either — which is precisely why production has been doing | |
| # sequential scans instead of using idx_embeddings_hnsw. Create them | |
| # explicitly for adopted databases. | |
| logger.info("Ensuring vector/trigram indexes on the adopted schema") | |
| ensure_indexes() | |
| else: | |
| logger.info("Fresh database — creating schema from the baseline") | |
| command.upgrade(cfg, "head") | |
| logger.info("Migrations complete") | |
| if __name__ == "__main__": | |
| try: | |
| run() | |
| except Exception as e: | |
| logger.error(f"Migration failed: {e}", exc_info=True) | |
| sys.exit(1) | |