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
This commit is contained in:
Claude 2026-04-25 22:19:25 +00:00
parent 87d2c0d13b
commit 0723407a6d
No known key found for this signature in database
4 changed files with 52 additions and 33 deletions

View File

@ -126,7 +126,7 @@ async def alert_stats(tenant_id: str):
@router.get("/digest", summary="Generate Arabic alert digest")
async def generate_digest(tenant_id: str, user_id: Optional[str] = None,
period: str = Query(default="daily", regex="^(daily|weekly)$")):
period: str = Query(default="daily", pattern="^(daily|weekly)$")):
return await get_alert_delivery().generate_digest(tenant_id, user_id, period)

View File

@ -70,13 +70,24 @@ async def get_db():
async def init_db():
import app.models # noqa: F401 — register all models on Base.metadata before create_all
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")
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

View File

@ -50,18 +50,22 @@ async def lifespan(app: FastAPI):
stop_event = asyncio.Event()
async def _self_improvement_worker():
await asyncio.sleep(30)
while not stop_event.is_set():
self_improvement_flow.run(
tenant_id="system_tenant",
input_state={
"signals": [],
"bottlenecks": [],
"experiments": [{"name": "always-on-ab-loop"}],
"ab_results": {},
"governance_passed": True,
"promoted": True,
},
)
try:
self_improvement_flow.run(
tenant_id="system_tenant",
input_state={
"signals": [],
"bottlenecks": [],
"experiments": [{"name": "always-on-ab-loop"}],
"ab_results": {},
"governance_passed": True,
"promoted": True,
},
)
except Exception:
pass
await asyncio.sleep(max(60, settings.SELF_IMPROVEMENT_INTERVAL_SECONDS))
worker_task = asyncio.create_task(_self_improvement_worker())
@ -72,16 +76,20 @@ async def lifespan(app: FastAPI):
print(f" LLM Primary: {settings.LLM_PRIMARY_PROVIDER}")
print(f" LLM Fallback: {settings.LLM_FALLBACK_PROVIDER}")
# Initialize PostHog
from app.services.posthog_client import get_posthog
ph = get_posthog()
print(f" PostHog: {'enabled' if ph._enabled else 'disabled (no API key)'}")
try:
from app.services.posthog_client import get_posthog
ph = get_posthog()
print(f" PostHog: {'enabled' if getattr(ph, '_enabled', False) else 'disabled (no API key)'}")
except Exception as e:
print(f" PostHog: init failed ({e})")
# Initialize DLQ
from app.services.dlq import dlq
print(" DLQ: initialized")
if IS_SQLITE:
await init_db()
try:
from app.services.dlq import dlq # noqa: F841
print(" DLQ: initialized")
except Exception as e:
print(f" DLQ: init failed ({e})")
await init_db()
yield
# Shutdown
stop_event.set()

View File

@ -115,8 +115,8 @@ class WhatsAppMessenger:
"""Send messages via Ultramsg API."""
def __init__(self):
self.instance_id = os.getenv("ULTRAMSG_INSTANCE_ID", "instance168132")
self.token = os.getenv("ULTRAMSG_TOKEN", "7azj2ss74wpg9jwp")
self.instance_id = os.getenv("ULTRAMSG_INSTANCE_ID", "")
self.token = os.getenv("ULTRAMSG_TOKEN", "")
self.api_base = f"https://api.ultramsg.com/{self.instance_id}"
async def send_message(self, phone: str, message: str) -> Dict: