"""Background rebuild loop for the export snapshot.
Same shape as the other lifespan loops (cleanup, recall, pending sweep): one
task, never allowed to die, logging and retrying on the next tick. The first
build happens at startup, so a restart is also a refresh.
"""
import asyncio
import structlog
from navi.config import settings
from navi.export.builder import build_export
log = structlog.get_logger()
_MIN_INTERVAL_SECONDS = 15 * 60
def export_interval_seconds() -> float:
return max(_MIN_INTERVAL_SECONDS, settings.navi_export_interval_hours * 3600)
async def export_loop(store) -> None:
"""Rebuild the snapshot every `navi_export_interval_hours`."""
interval = export_interval_seconds()
while True:
try:
pool = await store._get_pool()
await build_export(pool)
except asyncio.CancelledError:
raise
except Exception:
# A failed build leaves the previous file and manifest in place:
# built_at keeps telling the truth, and the consumer sees the
# snapshot getting older instead of silently stale-new.
log.exception("export.build_failed")
await asyncio.sleep(interval)