system-prompts-and-models-o.../salesflow-saas/backend/app/database.py
Claude 0723407a6d
fix: harden Railway startup — fault-tolerant lifespan + DB retry + credential cleanup
- Wrap PostHog/DLQ init in try/except so startup survives missing services
- Delay self-improvement worker 30s to reduce startup load
- Run init_db() for ALL database types (was SQLite-only, skipping PostgreSQL)
- Add 3-attempt retry with backoff in init_db() for Railway DB startup race
- Fix FastAPI deprecation: regex → pattern in intelligence.py
- Remove hardcoded Ultramsg credentials from auto_pipeline.py

https://claude.ai/code/session_01W1rJthWDkasijTdXCfxVHs
2026-04-25 22:19:25 +00:00

94 lines
2.8 KiB
Python

import os
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy import text
def _get_db_url() -> str:
url = os.environ.get("DATABASE_URL", "")
if not url:
for env_file in [".env", "../.env"]:
try:
with open(env_file) as f:
for line in f:
if line.strip().startswith("DATABASE_URL="):
url = line.strip().split("=", 1)[1]
break
except FileNotFoundError:
continue
if not url:
return "sqlite+aiosqlite:///./dealix.db"
# Railway Postgres gives postgres:// but SQLAlchemy needs postgresql+asyncpg://
if url.startswith("postgres://"):
url = url.replace("postgres://", "postgresql+asyncpg://", 1)
elif url.startswith("postgresql://"):
url = url.replace("postgresql://", "postgresql+asyncpg://", 1)
return url
_DB_URL = _get_db_url()
IS_SQLITE = "sqlite" in _DB_URL.lower()
if IS_SQLITE:
engine = create_async_engine(
_DB_URL,
echo=False,
connect_args={"check_same_thread": False},
)
else:
engine = create_async_engine(
_DB_URL,
echo=False,
pool_size=20,
max_overflow=10,
pool_pre_ping=True,
)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
# Aliases for backward compatibility with workers
SessionLocal = async_session
async_session_factory = async_session
class Base(DeclarativeBase):
pass
async def get_db():
async with async_session() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
async def init_db():
import app.models # noqa: F401 — register all models on Base.metadata before create_all
for attempt in range(3):
try:
async with engine.begin() as conn:
if not IS_SQLITE:
for ext in ["CREATE EXTENSION IF NOT EXISTS vector",
"CREATE EXTENSION IF NOT EXISTS pg_trgm"]:
try:
await conn.execute(text(ext))
except Exception:
pass
await conn.run_sync(Base.metadata.create_all)
print("✅ Database initialized")
return
except Exception as e:
if attempt < 2:
import asyncio
print(f"⚠️ DB init attempt {attempt + 1} failed: {e}, retrying in 3s...")
await asyncio.sleep(3)
else:
print(f"❌ DB init failed after 3 attempts: {e}")
raise