完全移除预热(prewarm)功能

- 删除 src/utils/prewarm_dashboard.py
- 删除 scripts/prewarm_dashboard_cache.py、prewarm_dashboard_worker.py
- docker-compose.yml 移除 polyweather_prewarm 服务
- runtime_coordinator.py 移除预热循环启动逻辑
- web/core.py 移除 prewarm status 上报
- web/routers/system.py 移除 /api/system/prewarm 端点
- web/services/system_api.py 移除 run_system_prewarm
- city_runtime.py DEFAULT_PREWARM_CITIES 改名为 DEFAULT_STATUS_CITIES
- 清理 env.example、测试、VPS .env 中的预热配置
This commit is contained in:
2569718930@qq.com
2026-05-17 22:43:17 +08:00
parent 22e2409e13
commit bd21b05fbc
12 changed files with 4 additions and 713 deletions
-1
View File
@@ -141,7 +141,6 @@ POLYWEATHER_SCAN_AI_MAX_ROWS=40
POLYWEATHER_SCAN_AI_MAX_TOKENS=3200
POLYWEATHER_SCAN_CITY_AI_MAX_TOKENS=900
POLYWEATHER_SCAN_AI_PROXY_TIMEOUT_MS=55000
POLYWEATHER_PREWARM_CITIES=ankara,istanbul,shanghai,beijing,shenzhen,guangzhou,wuhan,chengdu,chongqing,hong kong,taipei,singapore,tokyo,seoul,busan,london,paris,madrid
POLYWEATHER_CITY_SUMMARY_CACHE_TTL_SEC=1800
POLYWEATHER_CITY_PANEL_CACHE_TTL_SEC=1800
POLYWEATHER_CITY_NEARBY_CACHE_TTL_SEC=1800
-11
View File
@@ -34,14 +34,3 @@ services:
# UID/GID are mainly useful on Linux hosts to avoid root-owned output files.
user: "${UID:-1000}:${GID:-1000}"
polyweather_prewarm:
<<: *polyweather-base
container_name: polyweather_prewarm
restart: unless-stopped
profiles: ["workers"]
command: python scripts/prewarm_dashboard_worker.py --include-detail --include-market
volumes:
- ${POLYWEATHER_RUNTIME_DATA_DIR:-/var/lib/polyweather}:/var/lib/polyweather
- ${POLYWEATHER_RUNTIME_DATA_DIR:-/var/lib/polyweather}:/app/data
user: "${UID:-1000}:${GID:-1000}"
-49
View File
@@ -1,49 +0,0 @@
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
def parse_args() -> argparse.Namespace:
from src.utils.prewarm_dashboard import DEFAULT_CITIES
parser = argparse.ArgumentParser(
description="Prewarm PolyWeather summary/detail/market caches for selected cities.",
)
parser.add_argument(
"--base-url",
default=os.getenv("POLYWEATHER_BACKEND_URL", "http://127.0.0.1:8000"),
help="Backend base URL, defaults to POLYWEATHER_BACKEND_URL or http://127.0.0.1:8000",
)
parser.add_argument(
"--cities",
default=",".join(DEFAULT_CITIES),
help="Comma-separated city names to prewarm",
)
parser.add_argument("--force-refresh", action="store_true")
parser.add_argument("--include-detail", action="store_true")
parser.add_argument("--include-market", action="store_true")
return parser.parse_args()
def main() -> int:
args = parse_args()
from src.utils.prewarm_dashboard import run_prewarm
return run_prewarm(
base_url=args.base_url,
cities=args.cities,
force_refresh=bool(args.force_refresh),
include_detail=bool(args.include_detail),
include_market=bool(args.include_market),
)
if __name__ == "__main__":
raise SystemExit(main())
-65
View File
@@ -1,65 +0,0 @@
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
def parse_args() -> argparse.Namespace:
from src.utils.prewarm_dashboard import DEFAULT_CITIES
parser = argparse.ArgumentParser(
description="Run a background dashboard prewarm worker for hot PolyWeather cities.",
)
parser.add_argument(
"--base-url",
default=os.getenv("POLYWEATHER_BACKEND_URL", "http://127.0.0.1:8000"),
help="Backend base URL, defaults to POLYWEATHER_BACKEND_URL or http://127.0.0.1:8000",
)
parser.add_argument(
"--cities",
default=",".join(DEFAULT_CITIES),
help="Comma-separated city names to prewarm",
)
parser.add_argument(
"--interval-sec",
type=int,
default=int(os.getenv("POLYWEATHER_PREWARM_INTERVAL_SEC", "300")),
help="Worker interval in seconds",
)
parser.add_argument(
"--jitter-sec",
type=int,
default=int(os.getenv("POLYWEATHER_PREWARM_JITTER_SEC", "20")),
help="Random jitter added to each loop in seconds",
)
parser.add_argument("--force-refresh", action="store_true")
parser.add_argument("--include-detail", action="store_true")
parser.add_argument("--include-market", action="store_true")
parser.add_argument("--once", action="store_true")
return parser.parse_args()
def main() -> int:
args = parse_args()
from src.utils.prewarm_dashboard import run_worker_loop
return run_worker_loop(
base_url=args.base_url,
cities=args.cities,
interval_sec=args.interval_sec,
jitter_sec=args.jitter_sec,
force_refresh=bool(args.force_refresh),
include_detail=bool(args.include_detail),
include_market=bool(args.include_market),
once=bool(args.once),
)
if __name__ == "__main__":
raise SystemExit(main())
-27
View File
@@ -84,7 +84,6 @@ class StartupCoordinator:
def start_all(self) -> RuntimeStatus:
loops = [
self._start_airport_high_freq_loop(),
self._start_dashboard_prewarm_loop(),
self._start_polygon_wallet_loop(),
self._start_polymarket_wallet_activity_loop(),
self._start_weekly_reward_loop(),
@@ -175,32 +174,6 @@ class StartupCoordinator:
),
)
def _start_dashboard_prewarm_loop(self) -> LoopStatus:
enabled = _env_bool("POLYWEATHER_DASHBOARD_PREWARM_ENABLED", False)
interval = max(30, _env_int("POLYWEATHER_PREWARM_INTERVAL_SEC", 300))
jitter = max(0, _env_int("POLYWEATHER_PREWARM_JITTER_SEC", 20))
cities_count = _parse_csv_count(os.getenv("POLYWEATHER_PREWARM_CITIES")) or 14
details = {
"interval_sec": interval,
"jitter_sec": jitter,
"cities_count": cities_count,
"include_detail": _env_bool("POLYWEATHER_PREWARM_INCLUDE_DETAIL", True),
"include_market": _env_bool("POLYWEATHER_PREWARM_INCLUDE_MARKET", True),
"force_refresh": _env_bool("POLYWEATHER_PREWARM_FORCE_REFRESH", False),
"base_url": str(os.getenv("POLYWEATHER_BACKEND_URL") or "http://127.0.0.1:8000").strip(),
}
validation_error = None
if not str(os.getenv("POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN") or "").strip():
validation_error = "missing_POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN"
return self._start_with_validation(
key="dashboard_prewarm",
label="站点面板预热",
configured_enabled=enabled,
details=details,
validation_error=validation_error,
starter=lambda: import_module("src.utils.prewarm_dashboard").start_prewarm_worker_thread(),
)
def _start_polygon_wallet_loop(self) -> LoopStatus:
enabled = _env_bool("POLYGON_WALLET_WATCH_ENABLED", False)
chat_ids = get_telegram_chat_ids_from_env()
-418
View File
@@ -1,418 +0,0 @@
from __future__ import annotations
import json
import os
import random
import threading
import time
from datetime import datetime
from typing import Any, Dict, Optional
import httpx
from loguru import logger
from src.database.db_manager import DBManager
DEFAULT_CITIES = [
"ankara",
"istanbul",
"shanghai",
"beijing",
"shenzhen",
"guangzhou",
"qingdao",
"wuhan",
"chengdu",
"chongqing",
"hong kong",
"taipei",
"singapore",
"tokyo",
"seoul",
"busan",
"london",
"paris",
"madrid",
"amsterdam",
"helsinki",
"lau fau shan",
"new york",
"los angeles",
"chicago",
"denver",
"atlanta",
"miami",
"san francisco",
"houston",
"dallas",
"austin",
"seattle",
]
_RUNTIME_LOCK = threading.Lock()
_WORKER_THREAD: Optional[threading.Thread] = None
_DB = DBManager()
_RUNTIME_STATE_KEY = "dashboard_prewarm"
_RUNTIME_STATE: Dict[str, Any] = {
"cycle_count": 0,
"success_count": 0,
"failure_count": 0,
"last_started_at": None,
"last_finished_at": None,
"last_duration_sec": None,
"last_success": None,
"last_http_status": None,
"last_error": None,
"last_requested_cities": [],
"last_requested_city_count": 0,
"last_include_detail": False,
"last_include_market": False,
"last_force_refresh": False,
"last_warmed_count": 0,
"last_summary_ok": 0,
"last_detail_ok": 0,
"last_market_ok": 0,
"last_failed_count": 0,
"last_heartbeat_ts": None,
"writer_mode": None,
"writer_pid": None,
"writer_thread_name": None,
}
def _truthy_env(value: str, default: bool = False) -> bool:
raw = str(os.getenv(value) or "").strip().lower()
if not raw:
return default
return raw in {"1", "true", "yes", "on"}
def _parse_cities(value: str) -> list[str]:
items = [item.strip() for item in str(value or "").split(",")]
return [item for item in items if item]
def _update_runtime_state(**kwargs: Any) -> None:
with _RUNTIME_LOCK:
_RUNTIME_STATE.update(kwargs)
def _runtime_mode() -> str:
current = threading.current_thread()
if current.name == "dashboard-prewarm-worker":
return "embedded_thread"
if str(os.getenv("POLYWEATHER_PREWARM_WORKER_MODE") or "").strip():
return str(os.getenv("POLYWEATHER_PREWARM_WORKER_MODE") or "").strip().lower()
return "standalone_process"
def _snapshot_runtime_state() -> Dict[str, Any]:
with _RUNTIME_LOCK:
snapshot = dict(_RUNTIME_STATE)
snapshot["last_heartbeat_ts"] = time.time()
snapshot["writer_mode"] = _runtime_mode()
snapshot["writer_pid"] = os.getpid()
snapshot["writer_thread_name"] = threading.current_thread().name
return snapshot
def _persist_runtime_state() -> Dict[str, Any]:
snapshot = _snapshot_runtime_state()
with _RUNTIME_LOCK:
_RUNTIME_STATE.update(
{
"last_heartbeat_ts": snapshot["last_heartbeat_ts"],
"writer_mode": snapshot["writer_mode"],
"writer_pid": snapshot["writer_pid"],
"writer_thread_name": snapshot["writer_thread_name"],
}
)
try:
_DB.set_payment_runtime_state(_RUNTIME_STATE_KEY, snapshot)
except Exception as exc:
logger.debug("dashboard prewarm runtime persist failed: {}", exc)
return snapshot
def _record_prewarm_result(
*,
ok: bool,
duration_sec: float,
http_status: Optional[int],
error: Optional[str],
warmed_count: int,
summary_ok: int,
detail_ok: int,
market_ok: int,
failed_count: int,
) -> None:
with _RUNTIME_LOCK:
_RUNTIME_STATE["cycle_count"] = int(_RUNTIME_STATE.get("cycle_count") or 0) + 1
if ok:
_RUNTIME_STATE["success_count"] = int(_RUNTIME_STATE.get("success_count") or 0) + 1
else:
_RUNTIME_STATE["failure_count"] = int(_RUNTIME_STATE.get("failure_count") or 0) + 1
_RUNTIME_STATE["last_finished_at"] = datetime.now().isoformat(timespec="seconds")
_RUNTIME_STATE["last_duration_sec"] = round(float(duration_sec), 2)
_RUNTIME_STATE["last_success"] = bool(ok)
_RUNTIME_STATE["last_http_status"] = http_status
_RUNTIME_STATE["last_error"] = error
_RUNTIME_STATE["last_warmed_count"] = int(warmed_count or 0)
_RUNTIME_STATE["last_summary_ok"] = int(summary_ok or 0)
_RUNTIME_STATE["last_detail_ok"] = int(detail_ok or 0)
_RUNTIME_STATE["last_market_ok"] = int(market_ok or 0)
_RUNTIME_STATE["last_failed_count"] = int(failed_count or 0)
_persist_runtime_state()
def _parse_iso_timestamp(value: Any) -> float:
text = str(value or "").strip()
if not text:
return 0.0
try:
return datetime.fromisoformat(text).timestamp()
except Exception:
return 0.0
def _runtime_sort_key(payload: Dict[str, Any]) -> float:
if not isinstance(payload, dict):
return 0.0
candidates = [
float(payload.get("last_heartbeat_ts") or 0.0),
_parse_iso_timestamp(payload.get("last_finished_at")),
_parse_iso_timestamp(payload.get("last_started_at")),
]
return max(candidates)
def _load_shared_runtime_state() -> Dict[str, Any]:
try:
payload = _DB.get_payment_runtime_state(_RUNTIME_STATE_KEY)
except Exception as exc:
logger.debug("dashboard prewarm runtime load failed: {}", exc)
return {}
return payload if isinstance(payload, dict) else {}
def get_prewarm_runtime_summary() -> Dict[str, Any]:
configured_cities = _parse_cities(str(os.getenv("POLYWEATHER_PREWARM_CITIES") or ",".join(DEFAULT_CITIES)))
with _RUNTIME_LOCK:
local_runtime = dict(_RUNTIME_STATE)
shared_runtime = _load_shared_runtime_state()
runtime = local_runtime
if _runtime_sort_key(shared_runtime) > _runtime_sort_key(local_runtime):
runtime = shared_runtime
interval_sec = max(30, int(os.getenv("POLYWEATHER_PREWARM_INTERVAL_SEC", "300")))
jitter_sec = max(0, int(os.getenv("POLYWEATHER_PREWARM_JITTER_SEC", "20")))
heartbeat_age_sec = None
last_heartbeat_ts = float(runtime.get("last_heartbeat_ts") or 0.0)
if last_heartbeat_ts > 0:
heartbeat_age_sec = max(0.0, time.time() - last_heartbeat_ts)
shared_alive = bool(
last_heartbeat_ts > 0
and heartbeat_age_sec is not None
and heartbeat_age_sec <= float(interval_sec + jitter_sec + 90)
)
return {
"enabled": _truthy_env("POLYWEATHER_DASHBOARD_PREWARM_ENABLED", False),
"base_url": str(os.getenv("POLYWEATHER_BACKEND_URL") or "http://127.0.0.1:8000").strip(),
"configured_cities": configured_cities,
"configured_city_count": len(configured_cities),
"interval_sec": interval_sec,
"jitter_sec": jitter_sec,
"include_detail": _truthy_env("POLYWEATHER_PREWARM_INCLUDE_DETAIL", True),
"include_market": _truthy_env("POLYWEATHER_PREWARM_INCLUDE_MARKET", False),
"force_refresh": _truthy_env("POLYWEATHER_PREWARM_FORCE_REFRESH", False),
"thread_alive": bool(_WORKER_THREAD and _WORKER_THREAD.is_alive()) or shared_alive,
"heartbeat_age_sec": None if heartbeat_age_sec is None else round(heartbeat_age_sec, 2),
"runtime": runtime,
}
def run_prewarm(
*,
base_url: str,
cities: str,
force_refresh: bool,
include_detail: bool,
include_market: bool,
) -> int:
token = str(os.getenv("POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN") or "").strip()
requested_cities = _parse_cities(cities)
started = time.perf_counter()
_update_runtime_state(
last_started_at=datetime.now().isoformat(timespec="seconds"),
last_requested_cities=requested_cities,
last_requested_city_count=len(requested_cities),
last_include_detail=bool(include_detail),
last_include_market=bool(include_market),
last_force_refresh=bool(force_refresh),
last_error=None,
last_http_status=None,
)
_persist_runtime_state()
if not token:
_record_prewarm_result(
ok=False,
duration_sec=time.perf_counter() - started,
http_status=None,
error="missing_backend_token",
warmed_count=0,
summary_ok=0,
detail_ok=0,
market_ok=0,
failed_count=0,
)
print(
json.dumps(
{
"ok": False,
"reason": "missing_backend_token",
"detail": "POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN is required",
},
ensure_ascii=False,
)
)
return 1
try:
with httpx.Client(timeout=180, follow_redirects=True) as client:
response = client.post(
f"{base_url.rstrip('/')}/api/system/prewarm",
params={
"cities": cities,
"force_refresh": str(bool(force_refresh)).lower(),
"include_detail": str(bool(include_detail)).lower(),
"include_market": str(bool(include_market)).lower(),
},
headers={
"Accept": "application/json",
"x-polyweather-entitlement": token,
},
)
try:
payload = response.json()
except Exception:
payload = {"ok": False, "raw": (response.text or "")[:500]}
warmed_count = int((payload or {}).get("count") or 0) if isinstance(payload, dict) else 0
summary_ok = int((payload or {}).get("summary_ok") or 0) if isinstance(payload, dict) else 0
detail_ok = int((payload or {}).get("detail_ok") or 0) if isinstance(payload, dict) else 0
market_ok = int((payload or {}).get("market_ok") or 0) if isinstance(payload, dict) else 0
failed_count = int((payload or {}).get("failed_count") or 0) if isinstance(payload, dict) else 0
ok = bool(response.is_success and (not isinstance(payload, dict) or payload.get("ok", True)))
_record_prewarm_result(
ok=ok,
duration_sec=time.perf_counter() - started,
http_status=response.status_code,
error=None if ok else str((payload or {}).get("detail") or (payload or {}).get("reason") or response.text[:200]),
warmed_count=warmed_count,
summary_ok=summary_ok,
detail_ok=detail_ok,
market_ok=market_ok,
failed_count=failed_count,
)
print(json.dumps(payload, ensure_ascii=False, indent=2))
return 0 if response.is_success else 1
except Exception as exc:
_record_prewarm_result(
ok=False,
duration_sec=time.perf_counter() - started,
http_status=None,
error=str(exc),
warmed_count=0,
summary_ok=0,
detail_ok=0,
market_ok=0,
failed_count=0,
)
print(json.dumps({"ok": False, "reason": "request_failed", "detail": str(exc)}, ensure_ascii=False, indent=2))
return 1
def run_worker_loop(
*,
base_url: str,
cities: str,
interval_sec: int,
jitter_sec: int,
force_refresh: bool,
include_detail: bool,
include_market: bool,
once: bool = False,
) -> int:
interval_sec = max(30, int(interval_sec))
jitter_sec = max(0, int(jitter_sec))
logger.info(
"dashboard prewarm worker started base_url={} cities={} interval_sec={} jitter_sec={} include_detail={} include_market={} force_refresh={} once={}",
base_url,
cities,
interval_sec,
jitter_sec,
bool(include_detail),
bool(include_market),
bool(force_refresh),
bool(once),
)
_persist_runtime_state()
while True:
started = time.perf_counter()
exit_code = run_prewarm(
base_url=base_url,
cities=cities,
force_refresh=bool(force_refresh),
include_detail=bool(include_detail),
include_market=bool(include_market),
)
elapsed = time.perf_counter() - started
logger.info(
"dashboard prewarm worker cycle exit_code={} elapsed_sec={} finished_at={}",
exit_code,
round(elapsed, 2),
datetime.now().isoformat(timespec="seconds"),
)
if once:
return exit_code
sleep_sec = max(5.0, interval_sec - elapsed)
if jitter_sec > 0:
sleep_sec += random.randint(0, jitter_sec)
logger.info("dashboard prewarm worker sleeping sleep_sec={}", round(sleep_sec, 2))
time.sleep(sleep_sec)
def start_prewarm_worker_thread() -> Optional[threading.Thread]:
enabled = str(os.getenv("POLYWEATHER_DASHBOARD_PREWARM_ENABLED") or "").strip().lower()
if enabled not in {"1", "true", "yes", "on"}:
return None
base_url = str(os.getenv("POLYWEATHER_BACKEND_URL") or "http://127.0.0.1:8000").strip()
cities = str(os.getenv("POLYWEATHER_PREWARM_CITIES") or ",".join(DEFAULT_CITIES)).strip()
interval_sec = int(os.getenv("POLYWEATHER_PREWARM_INTERVAL_SEC", "300"))
jitter_sec = int(os.getenv("POLYWEATHER_PREWARM_JITTER_SEC", "20"))
force_refresh = str(os.getenv("POLYWEATHER_PREWARM_FORCE_REFRESH") or "").strip().lower() in {"1", "true", "yes", "on"}
include_detail = str(os.getenv("POLYWEATHER_PREWARM_INCLUDE_DETAIL", "true")).strip().lower() in {"1", "true", "yes", "on"}
include_market = str(os.getenv("POLYWEATHER_PREWARM_INCLUDE_MARKET", "false")).strip().lower() in {"1", "true", "yes", "on"}
thread = threading.Thread(
target=run_worker_loop,
kwargs={
"base_url": base_url,
"cities": cities,
"interval_sec": interval_sec,
"jitter_sec": jitter_sec,
"force_refresh": force_refresh,
"include_detail": include_detail,
"include_market": include_market,
"once": False,
},
name="dashboard-prewarm-worker",
daemon=True,
)
thread.start()
global _WORKER_THREAD
_WORKER_THREAD = thread
return thread
-48
View File
@@ -6,7 +6,6 @@ import web.routes as routes
import web.scan_terminal_cache as scan_terminal_cache
import web.scan_terminal_service as scan_terminal_service
from web.scan_terminal_cache import scan_terminal_cache_key
from src.database.db_manager import DBManager
from src.database.runtime_state import TruthRecordRepository, TrainingFeatureRecordRepository
@@ -38,7 +37,6 @@ def test_system_status_returns_summary_shape():
assert payload['probability']['rollout']['decision']['decision'] in {'hold', 'observe', 'promote'}
assert 'training_data' in payload
assert 'station_networks' in payload
assert 'prewarm' in payload
assert 'truth_records' in payload['training_data']
assert 'training_features' in payload['training_data']
assert 'city_coverage' in payload['training_data']
@@ -46,55 +44,9 @@ def test_system_status_returns_summary_shape():
assert 'artifacts' in payload['training_data']
assert 'metar_entries' in payload['cache']
assert 'nmc_entries' in payload['cache']
assert 'runtime' in payload['prewarm']
assert 'last_summary_ok' in payload['prewarm']['runtime']
assert 'cities_count' in payload
def test_system_status_reads_shared_prewarm_runtime(monkeypatch):
monkeypatch.setenv("POLYWEATHER_DASHBOARD_PREWARM_ENABLED", "true")
monkeypatch.setenv("POLYWEATHER_PREWARM_INTERVAL_SEC", "300")
monkeypatch.setenv("POLYWEATHER_PREWARM_JITTER_SEC", "20")
DBManager().set_payment_runtime_state(
"dashboard_prewarm",
{
"cycle_count": 3,
"success_count": 3,
"failure_count": 0,
"last_started_at": "2026-04-08T12:00:00",
"last_finished_at": "2026-04-08T12:00:05",
"last_duration_sec": 5.0,
"last_success": True,
"last_http_status": 200,
"last_error": None,
"last_requested_cities": ["shanghai", "beijing"],
"last_requested_city_count": 2,
"last_include_detail": True,
"last_include_market": True,
"last_force_refresh": False,
"last_warmed_count": 2,
"last_summary_ok": 2,
"last_detail_ok": 2,
"last_market_ok": 2,
"last_failed_count": 0,
"last_heartbeat_ts": __import__("time").time(),
"writer_mode": "standalone_process",
"writer_pid": 12345,
"writer_thread_name": "MainThread",
},
)
response = client.get('/api/system/status')
assert response.status_code == 200
payload = response.json()
assert payload["prewarm"]["enabled"] is True
assert payload["prewarm"]["thread_alive"] is True
assert payload["prewarm"]["runtime"]["cycle_count"] >= 3
assert payload["prewarm"]["runtime"]["last_summary_ok"] == 2
def test_metrics_endpoint_returns_prometheus_payload():
response = client.get('/metrics')
assert response.status_code == 200
-2
View File
@@ -21,7 +21,6 @@ from src.data_collection.country_networks import provider_coverage_summary
from src.data_collection.city_risk_profiles import CITY_RISK_PROFILES # noqa: F401
from src.data_collection.polymarket_readonly import PolymarketReadOnlyLayer
from src.auth.supabase_entitlement import SUPABASE_ENTITLEMENT, extract_bearer_token
from src.utils.prewarm_dashboard import get_prewarm_runtime_summary
from src.utils.metrics import (
build_metrics_summary,
counter_inc,
@@ -836,6 +835,5 @@ def build_system_status_payload() -> Dict[str, Any]:
"probability": _probability_summary(),
"training_data": _training_data_summary(),
"station_networks": provider_coverage_summary(),
"prewarm": get_prewarm_runtime_summary(),
"cities_count": len(CITIES),
}
-18
View File
@@ -11,7 +11,6 @@ from web.services.system_api import (
get_prometheus_metrics_response,
get_system_cache_status,
get_system_status_payload,
run_system_prewarm,
run_system_priority_warm,
)
@@ -28,23 +27,6 @@ async def system_status():
return await get_system_status_payload()
@router.post("/api/system/prewarm")
async def system_prewarm(
request: Request,
cities: Optional[str] = None,
force_refresh: bool = False,
include_detail: bool = False,
include_market: bool = False,
):
return run_system_prewarm(
request,
cities=cities,
force_refresh=force_refresh,
include_detail=include_detail,
include_market=include_market,
)
@router.get("/api/system/cache-status")
async def system_cache_status(request: Request, cities: Optional[str] = None):
return get_system_cache_status(request, cities=cities)
+2 -2
View File
@@ -80,7 +80,7 @@ TRACKABLE_ANALYTICS_EVENTS = {
"checkout_succeeded",
}
DEFAULT_PREWARM_CITIES = [
DEFAULT_STATUS_CITIES = [
"ankara",
"istanbul",
"shanghai",
@@ -658,7 +658,7 @@ def _normalize_city_or_404(name: str) -> str:
def _normalize_city_list(raw: Optional[str]) -> list[str]:
if not raw:
return list(DEFAULT_PREWARM_CITIES)
return list(DEFAULT_STATUS_CITIES)
out: list[str] = []
for part in str(raw).split(","):
city = str(part or "").strip().lower().replace("-", " ")
+1 -1
View File
@@ -23,7 +23,7 @@ def _resolve_default_city(request: Request) -> Optional[str]:
cities_map = getattr(legacy_routes, "TIMEZONE_DEFAULT_CITIES", {})
if tz in cities_map:
return cities_map[tz]
return next(iter(getattr(legacy_routes, "DEFAULT_PREWARM_CITIES", [])), None)
return next(iter(getattr(legacy_routes, "DEFAULT_STATUS_CITIES", [])), None)
async def build_dashboard_init_payload(request: Request) -> Dict[str, Any]:
+1 -71
View File
@@ -26,81 +26,11 @@ async def get_system_status_payload() -> Dict[str, Any]:
return await run_in_threadpool(build_system_status_payload)
def run_system_prewarm(
request: Request,
*,
cities: Optional[str] = None,
force_refresh: bool = False,
include_detail: bool = False,
include_market: bool = False,
) -> Dict[str, Any]:
legacy_routes._assert_entitlement(request)
selected = legacy_routes._normalize_city_list(cities)
if not selected:
raise HTTPException(status_code=400, detail="No valid cities to prewarm")
started = time.perf_counter()
warmed: list[dict[str, object]] = []
failed: list[dict[str, object]] = []
summary_ok = 0
detail_ok = 0
market_ok = 0
for city in selected:
city_started = time.perf_counter()
try:
legacy_routes._refresh_city_summary_cache(city, force_refresh=force_refresh)
entry: dict[str, object] = {
"city": city,
"summary": True,
"duration_ms": round((time.perf_counter() - city_started) * 1000.0, 1),
}
summary_ok += 1
if include_detail:
legacy_routes._refresh_city_panel_cache(city, force_refresh=force_refresh)
entry["detail"] = True
detail_ok += 1
if include_market:
entry["market"] = False
warmed.append(entry)
except Exception as exc:
failed.append(
{
"city": city,
"error": str(exc),
"duration_ms": round((time.perf_counter() - city_started) * 1000.0, 1),
}
)
total_ms = round((time.perf_counter() - started) * 1000.0, 1)
logger.info(
"system prewarm finished count={} failed={} force_refresh={} include_detail={} include_market={} duration_ms={}",
len(warmed),
len(failed),
force_refresh,
include_detail,
include_market,
total_ms,
)
return {
"ok": len(failed) == 0,
"cities": selected,
"warmed": warmed,
"failed": failed,
"summary_ok": summary_ok,
"panel_ok": detail_ok,
"detail_ok": detail_ok,
"market_ok": market_ok,
"failed_count": len(failed),
"duration_ms": total_ms,
}
def get_system_cache_status(request: Request, cities: Optional[str] = None) -> Dict[str, Any]:
legacy_routes._assert_entitlement(request)
selected = legacy_routes._normalize_city_list(cities)
if not selected:
selected = list(legacy_routes.DEFAULT_PREWARM_CITIES)
selected = legacy_routes._normalize_city_list(None)
kinds = {
"summary": legacy_routes.CITY_SUMMARY_CACHE_TTL_SEC,
"panel": legacy_routes.CITY_PANEL_CACHE_TTL_SEC,