Files
openswarm/backend/config/Apps.py
T
Eric b6652b6722 [eric] backend: add per-lifespan boot timing to pin cold-start stalls
- debug(sub_app.name) is a no-op in packaged builds, so the packaged backend.log
  had zero per-SubApp markers and a cold stall could only be guessed at
- wrap each enter_async_context with time.perf_counter + a flushed [perf] print,
  plus a lifespans-total line, so a cold launch names the exact slow lifespan
- logging only, no functional change; loop logic validated warm
2026-06-17 03:22:10 -07:00

57 lines
2.1 KiB
Python

import os
import time
from fastapi import FastAPI, APIRouter
import debug
from uuid import uuid4
from typing import List
from contextlib import asynccontextmanager
from contextlib import AsyncExitStack
from typing import Callable
class SubApp:
def __init__(self, name:str, lifespan:Callable):
debug("START", name)
self.id = uuid4()
self.name = name
self.prefix = f"/api/{name}"
self.lifespan = lifespan
self.router = APIRouter()
debug("END")
def __str__(self):
return f"SubApp(name={self.name}, prefix={self.prefix}, id={self.id})"
class MainApp:
def __init__(self, sub_apps: List[SubApp]):
debug("START")
@asynccontextmanager
async def lifespan(app: FastAPI):
async with AsyncExitStack() as stack:
# [perf] per-lifespan boot timing. debug() is a no-op in the
# packaged build, so without this the packaged backend.log has no
# per-SubApp markers and a cold-start stall can only be guessed at.
# One perf_counter + flushed print per app pins exactly which
# lifespan (or the cold first-touch I/O entering it) dominates.
_boot_t0 = time.perf_counter()
for sub_app in sub_apps:
debug(sub_app.name)
_t0 = time.perf_counter()
await stack.enter_async_context(sub_app.lifespan())
print(f"[perf] lifespan {sub_app.name} t={(time.perf_counter() - _t0) * 1000:.0f}ms", flush=True)
print(f"[perf] lifespans-total t={(time.perf_counter() - _boot_t0) * 1000:.0f}ms", flush=True)
_port = os.environ.get("OPENSWARM_PORT", "8324")
print(f"\nCheck out the API docs at: http://127.0.0.1:{_port}/docs\n")
yield
self.app = FastAPI(lifespan=lifespan)
for sub_app in sub_apps:
self.app.include_router(
sub_app.router,
prefix=sub_app.prefix,
tags=[sub_app.name]
)
debug("END")