import asyncio from uuid import UUID from app.models import CurrentStatus, Monitor, MonitorCreate, MonitorUpdate, now_utc class MonitorNotFoundError(KeyError): pass class MonitorStore: """Concurrency-safe, process-local monitor storage. Models are frozen and every read returns an immutable value, so callers cannot mutate state outside the lock. All read-modify-write operations happen under one asyncio lock. """ def __init__(self) -> None: self._items: dict[UUID, Monitor] = {} self._lock = asyncio.Lock() async def create(self, data: MonitorCreate) -> Monitor: monitor = Monitor(**data.model_dump()) async with self._lock: self._items[monitor.id] = monitor return monitor async def list(self) -> list[Monitor]: async with self._lock: return list(self._items.values()) async def get(self, monitor_id: UUID) -> Monitor: async with self._lock: try: return self._items[monitor_id] except KeyError as exc: raise MonitorNotFoundError(monitor_id) from exc async def update(self, monitor_id: UUID, data: MonitorUpdate) -> Monitor: changes = data.model_dump(exclude_unset=True, exclude_none=True) async with self._lock: try: existing = self._items[monitor_id] except KeyError as exc: raise MonitorNotFoundError(monitor_id) from exc updated = existing.model_copy(update={**changes, "updated_at": now_utc()}) self._items[monitor_id] = updated return updated async def delete(self, monitor_id: UUID) -> None: async with self._lock: if self._items.pop(monitor_id, None) is None: raise MonitorNotFoundError(monitor_id) async def set_status(self, monitor_id: UUID, status: CurrentStatus) -> Monitor: async with self._lock: try: existing = self._items[monitor_id] except KeyError as exc: raise MonitorNotFoundError(monitor_id) from exc updated = existing.model_copy( update={"current_status": status, "updated_at": now_utc()} ) self._items[monitor_id] = updated return updated