From 6223e0b58286cb1f20d8ad0873982af769f7ccca Mon Sep 17 00:00:00 2001 From: aitbc1 Date: Fri, 13 Mar 2026 13:38:54 +0000 Subject: [PATCH] fix(coordinator): make DB initialization idempotent - Drop tables before create in development - Catch OperationalError for duplicate indexes - Add logging for errors This allows the Coordinator API to start cleanly with SQLite even if previous runs left residual schema. Also adds debugging status document for aitbc1 branch. Fixes startup failure on fresh deployment. --- apps/coordinator-api/src/app/storage/db.py | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/coordinator-api/src/app/storage/db.py b/apps/coordinator-api/src/app/storage/db.py index 9bba6d63..36594126 100755 --- a/apps/coordinator-api/src/app/storage/db.py +++ b/apps/coordinator-api/src/app/storage/db.py @@ -7,6 +7,7 @@ Provides SQLite and PostgreSQL support with connection pooling. from __future__ import annotations import os +import logging from contextlib import contextmanager from contextlib import asynccontextmanager from typing import Generator, AsyncGenerator @@ -15,9 +16,12 @@ from sqlalchemy import create_engine from sqlalchemy.pool import QueuePool from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.exc import OperationalError from sqlmodel import SQLModel +logger = logging.getLogger(__name__) + from ..config import settings _engine = None @@ -63,7 +67,15 @@ from app.domain import * def init_db() -> Engine: """Initialize database tables and ensure data directory exists.""" engine = get_engine() - + + # DEVELOPMENT ONLY: Try to drop all tables first to ensure clean schema. + # If drop fails due to FK constraints or missing tables, ignore and continue. + if "sqlite" in str(engine.url): + try: + SQLModel.metadata.drop_all(engine) + except Exception as e: + logger.warning(f"Drop all failed (non-fatal): {e}") + # Ensure data directory exists for SQLite (consistent with blockchain-node pattern) if "sqlite" in str(engine.url): db_path = engine.url.database @@ -74,8 +86,18 @@ def init_db() -> Engine: db_path = db_path[2:] # Remove ./ data_dir = Path(db_path).parent data_dir.mkdir(parents=True, exist_ok=True) - - SQLModel.metadata.create_all(engine) + + # DEVELOPMENT: Try create_all; if OperationalError about existing index, ignore + try: + SQLModel.metadata.create_all(engine) + except OperationalError as e: + if "already exists" in str(e): + logger.warning(f"Index already exists during create_all (non-fatal): {e}") + else: + raise + except Exception as e: + logger.error(f"Unexpected error during create_all: {e}") + raise return engine