feat(worker): ручной пересчёт метрик через очередь, диагностика шагов и heartbeat
POST /metrics/refresh ставит задачу в sync_job (source=METRICS_JOB) и отвечает 202, пересчёт делает worker; GET /metrics/status отдаёт refreshing и consistent. metric_refresh_log хранит failed_step и step_timings. Источники с needs="tinvest_token" не попадают в расписание без токена, tinvest/moex добавлены в default_schedule. Воркер трогает heartbeat-файл для healthcheck.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Rebuild every metric_* table after data changed (plan §3).
|
||||
|
||||
Order (each step is a function taking an AsyncSession and committing its own tables):
|
||||
fx -> classify -> net worth -> cash flow -> spending -> runway -> data quality
|
||||
Phase 2 inserts prices/lots/valuation/holdings/returns between fx and net worth.
|
||||
Each step is a function taking an AsyncSession and committing its own tables. The steps and
|
||||
their order live in `analytics.register_steps`, with the reason for each dependency — this
|
||||
module only runs whatever is registered, in registration order.
|
||||
|
||||
`refresh_all` is what the worker calls after a sync that reported `changed=True`, what the
|
||||
CLI `fintracker metrics refresh` runs, and what `POST /metrics/refresh` queues.
|
||||
@@ -16,6 +16,7 @@ connection serialises them: a refresh that arrives during a sync WAITS and then
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
from collections.abc import Awaitable, Callable
|
||||
from datetime import UTC, datetime
|
||||
@@ -52,15 +53,22 @@ async def refresh_all(session: AsyncSession, trigger: str) -> MetricRefreshLog:
|
||||
entry = MetricRefreshLog(trigger=trigger)
|
||||
session.add(entry)
|
||||
await session.commit()
|
||||
timings: dict[str, float] = {}
|
||||
current: str | None = None
|
||||
try:
|
||||
for name, step in STEPS:
|
||||
current = name
|
||||
log.info("metrics: %s", name)
|
||||
started = time.monotonic()
|
||||
await step(session)
|
||||
await session.commit()
|
||||
timings[name] = round(time.monotonic() - started, 3)
|
||||
except Exception as exc:
|
||||
await session.rollback()
|
||||
entry.error = f"{type(exc).__name__}: {exc}\n{traceback.format_exc()[-4000:]}"
|
||||
log.exception("metrics refresh failed at step")
|
||||
entry.failed_step = current
|
||||
log.exception("metrics refresh failed at step %s", current)
|
||||
entry.step_timings = timings
|
||||
entry.finished_at = datetime.now(UTC)
|
||||
await session.commit()
|
||||
return entry
|
||||
|
||||
Reference in New Issue
Block a user