2026-07-22 17:32:59 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import asyncio
|
2026-08-21 09:49:48 +08:00
|
|
|
import time
|
2026-07-22 17:32:59 +08:00
|
|
|
|
|
|
|
|
from app.core.config import get_settings
|
|
|
|
|
from app.core.logging import get_logger
|
2026-08-21 09:49:48 +08:00
|
|
|
from app.db.platform_store import get_platform_store
|
2026-07-22 17:32:59 +08:00
|
|
|
from app.modules.compute_gateway.sync import poll_compute_jobs_once
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def run_compute_poller() -> None:
|
|
|
|
|
settings = get_settings()
|
|
|
|
|
if settings.compute_mode == "simulator" or settings.compute_status_sync_mode != "polling":
|
|
|
|
|
logger.info("compute poller disabled", extra={"compute_mode": settings.compute_mode})
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
interval = max(3, settings.compute_poll_interval_seconds)
|
|
|
|
|
logger.info("compute poller started", extra={"interval_seconds": interval})
|
2026-08-21 09:49:48 +08:00
|
|
|
# PlatformStore may run additive schema checks against a remote PostgreSQL
|
|
|
|
|
# server on first use. Keep that startup work off the Uvicorn event loop so
|
|
|
|
|
# health checks and normal API requests can still respond while the DB is
|
|
|
|
|
# unavailable or slow.
|
|
|
|
|
store = None
|
|
|
|
|
last_failure_signature = ""
|
|
|
|
|
last_failure_logged_at = 0.0
|
|
|
|
|
await asyncio.sleep(1)
|
2026-07-22 17:32:59 +08:00
|
|
|
while True:
|
|
|
|
|
try:
|
2026-08-21 09:49:48 +08:00
|
|
|
if store is None:
|
|
|
|
|
store = await asyncio.to_thread(get_platform_store)
|
|
|
|
|
result = await poll_compute_jobs_once(store)
|
2026-08-12 15:21:23 +08:00
|
|
|
if result["failed"]:
|
2026-08-21 09:49:48 +08:00
|
|
|
signature = "|".join(sorted({
|
|
|
|
|
str(item.get("error") or "")[:120]
|
|
|
|
|
for item in result["failed"]
|
|
|
|
|
}))
|
|
|
|
|
now = time.monotonic()
|
|
|
|
|
if signature != last_failure_signature or now - last_failure_logged_at >= 300:
|
|
|
|
|
logger.warning(
|
|
|
|
|
"compute polling reported failures count=%d first_error=%s",
|
|
|
|
|
len(result["failed"]),
|
|
|
|
|
signature[:500],
|
|
|
|
|
)
|
|
|
|
|
last_failure_signature = signature
|
|
|
|
|
last_failure_logged_at = now
|
2026-08-12 15:21:23 +08:00
|
|
|
elif result["synced"]:
|
|
|
|
|
logger.debug("compute jobs synchronized", extra={"result": result})
|
2026-07-22 17:32:59 +08:00
|
|
|
except asyncio.CancelledError:
|
|
|
|
|
logger.info("compute poller stopped")
|
|
|
|
|
raise
|
|
|
|
|
except Exception as exc: # noqa: BLE001 - keep background polling alive
|
|
|
|
|
logger.exception("compute poller failed", extra={"error": str(exc)})
|
2026-08-21 09:49:48 +08:00
|
|
|
if "store" in locals() and isinstance(exc, (ConnectionError, TimeoutError)):
|
|
|
|
|
store = None
|
2026-07-22 17:32:59 +08:00
|
|
|
await asyncio.sleep(interval)
|