Files
webmail-client/backend/app/sync/supervisor.py
T

47 lines
1.5 KiB
Python
Raw Normal View History

2026-07-18 19:56:06 +02:00
"""Live-sync supervisor (refactor §4, transitional).
2026-07-17 23:20:34 +02:00
2026-07-18 19:56:06 +02:00
Runs inside the sync worker: on startup it launches one :class:`AccountRunner`
per live account. This step (v2 §9.2) only extracts the per-account loops into a
runner; account discovery is still startup-only and is replaced by the lease
claimer in a later step.
2026-07-17 23:20:34 +02:00
"""
from __future__ import annotations
import asyncio
import logging
import uuid
from typing import Any
from sqlalchemy import select
from app.db import session_scope
from app.models import Account
2026-07-18 19:56:06 +02:00
from app.sync.runner import AccountRunner
2026-07-17 23:20:34 +02:00
logger = logging.getLogger(__name__)
2026-07-18 19:56:06 +02:00
async def _live_account_ids() -> list[uuid.UUID]:
async with session_scope() as session:
return list(await session.scalars(select(Account.id).where(Account.state == "live")))
2026-07-17 23:20:34 +02:00
async def start_supervisor(ctx: dict[str, Any]) -> None:
2026-07-18 19:56:06 +02:00
"""Launch an AccountRunner per live account (called from the worker on_startup)."""
runners: dict[uuid.UUID, AccountRunner] = {}
for account_id in await _live_account_ids():
runner = AccountRunner(account_id)
runner.start()
runners[account_id] = runner
ctx["runners"] = runners
logger.info("live supervisor started for %d account(s)", len(runners))
2026-07-17 23:20:34 +02:00
async def stop_supervisor(ctx: dict[str, Any]) -> None:
2026-07-18 19:56:06 +02:00
"""Stop and tear down all account runners (called from the worker on_shutdown)."""
runners: dict[uuid.UUID, AccountRunner] = ctx.get("runners", {})
if runners:
await asyncio.gather(*(r.stop() for r in runners.values()), return_exceptions=True)
ctx["runners"] = {}