Accepted — 2026-04-01
ChannelManagerand the MPEG-TS / playout pipeline were ported from a synchronous StreamTV/ErsatzTV-style stack. They use a sync SQLAlchemySessionfromget_sync_session_factory()and run long-lived asyncio tasks that call into that stack.- FastAPI routes and newer code use async
AsyncSessionviaDepends(get_db). - The pattern prompt (EXStreamTV-Cursor-Pattern-Prompt.md) calls for async-only DB access in services; the codebase audit (see
AGENTS.md,docs/LESSONS_LEARNED.md) also warns against calling sync SQLAlchemy directly fromasync defwithoutrun_in_executor.
- Keep sync sessions inside the channel/stream worker path for now:
ChannelManager, playout resolution, and FFmpeg-adjacent code that already run in a dedicated concurrency model (async generators + sync DB callbacks). Migrating this surface toAsyncSessionwould require a wide refactor (everydb.query/session.executein the hot path) and retest of streaming stability. - Do not call sync session methods from inside
async defwithout offloading to a thread (asyncio.to_threadorrun_in_executor). New code should prefer repositories + AsyncSession at the API boundary (exstreamtv/patterns/repository/,ChannelRepository, etc.). - ID types: channel primary keys are integers in the ORM; public string IDs (e.g. command queue) remain at the API/FSM boundary but DB repositories use
intforChannel.idconsistently.
- Pros: Stable streaming path; incremental migration possible.
- Cons: Two session styles coexist; reviewers must ensure sync DB is not invoked directly from async handlers without a thread boundary.
- Follow-up: Optional future ADR for a full async ChannelManager once playout and pool managers share a single async session factory.
exstreamtv/streaming/channel_manager.pyexstreamtv/main.py(lifespan wiring)AGENTS.md(async lock + sync DB rules)EXStreamTV-UI-Architecture.md(backend remains FastAPI; UI is separatefrontend/)