Budget-tracker hydration helpers (Sprint B, v5.6.1).
Split from :mod:symfonic.core.observability.budget to keep each module
under the 300-LOC cap. The tracker imports :func:hydrate_from_store
and calls it before every check_budget so multi-replica deployments
see the same running total.
hydrate_from_store
async
hydrate_from_store(
tracker: TokenBudgetTracker, tenant_id: str
) -> None
Refresh the tracker's in-memory buckets from the persistent store.
No-op when no store is registered or when the store does not implement
the optional read_today / read_month hooks. Errors are logged
at WARNING and swallowed — a broken store must never wedge
check_budget.
Source code in src/symfonic/core/observability/budget_hydrate.py
| async def hydrate_from_store(
tracker: TokenBudgetTracker, tenant_id: str,
) -> None:
"""Refresh the tracker's in-memory buckets from the persistent store.
No-op when no store is registered or when the store does not implement
the optional ``read_today`` / ``read_month`` hooks. Errors are logged
at WARNING and swallowed — a broken store must never wedge
``check_budget``.
"""
store = tracker._store # noqa: SLF001 — same-package helper
if store is None:
return
read_today = getattr(store, "read_today", None)
read_month = getattr(store, "read_month", None)
if read_today is None and read_month is None:
return # Write-only store — no multi-replica hydration possible.
try:
if read_today is not None:
remote_day = await read_today(tenant_id)
if remote_day is not None:
async with tracker._get_lock(): # noqa: SLF001
tracker._daily[tenant_id] = remote_day # noqa: SLF001
if read_month is not None:
remote_month = await read_month(tenant_id, month_key())
if remote_month is not None:
async with tracker._get_lock(): # noqa: SLF001
tracker._monthly[tenant_id] = remote_month # noqa: SLF001
except Exception: # pragma: no cover - integration tested
logger.warning(
"BudgetStore hydration failed for tenant=%s "
"(falling back to in-memory state)",
tenant_id,
exc_info=True,
)
|