Schedule public snapshot refreshes and optimize historical loading

This commit is contained in:
devRaGonSa
2026-06-10 14:32:41 +02:00
parent 1d2af10458
commit ae5355c326
12 changed files with 1128 additions and 105 deletions

View File

@@ -21,6 +21,11 @@ DEFAULT_HISTORICAL_CRCON_RETRY_DELAY_SECONDS = 0.5
DEFAULT_HISTORICAL_REFRESH_INTERVAL_SECONDS = 1800
DEFAULT_HISTORICAL_REFRESH_OVERLAP_HOURS = 12
DEFAULT_HISTORICAL_SNAPSHOT_REFRESH_INTERVAL_SECONDS = 900
DEFAULT_PUBLIC_FULL_REFRESH_ENABLED = True
DEFAULT_PUBLIC_FULL_REFRESH_TIME = "06:00"
DEFAULT_PUBLIC_FULL_REFRESH_TIMEZONE = "Europe/Madrid"
DEFAULT_PUBLIC_RANKING_REFRESH_INTERVAL_SECONDS = 900
DEFAULT_PUBLIC_RECENT_MATCHES_REFRESH_INTERVAL_SECONDS = 60
DEFAULT_HISTORICAL_REFRESH_MAX_RETRIES = 2
DEFAULT_HISTORICAL_REFRESH_RETRY_DELAY_SECONDS = 30
DEFAULT_HISTORICAL_FULL_SNAPSHOT_EVERY_RUNS = 4
@@ -510,6 +515,58 @@ def get_rcon_historical_capture_retry_delay_seconds() -> int:
return retry_delay_seconds
def get_public_full_refresh_enabled() -> bool:
"""Return whether the runner should execute the daily public full refresh."""
return _read_bool_env(
"HLL_PUBLIC_FULL_REFRESH_ENABLED",
default=DEFAULT_PUBLIC_FULL_REFRESH_ENABLED,
)
def get_public_full_refresh_time() -> str:
"""Return the local HH:MM time for the daily public full refresh."""
configured_value = os.getenv(
"HLL_PUBLIC_FULL_REFRESH_TIME",
DEFAULT_PUBLIC_FULL_REFRESH_TIME,
).strip()
parts = configured_value.split(":")
if len(parts) != 2:
raise ValueError("HLL_PUBLIC_FULL_REFRESH_TIME must use HH:MM format.")
hour, minute = (int(part) for part in parts)
if hour < 0 or hour > 23 or minute < 0 or minute > 59:
raise ValueError("HLL_PUBLIC_FULL_REFRESH_TIME must use HH:MM format.")
return f"{hour:02d}:{minute:02d}"
def get_public_full_refresh_timezone() -> str:
"""Return the IANA timezone for the daily public full refresh."""
configured_value = os.getenv(
"HLL_PUBLIC_FULL_REFRESH_TIMEZONE",
DEFAULT_PUBLIC_FULL_REFRESH_TIMEZONE,
).strip()
if not configured_value:
raise ValueError("HLL_PUBLIC_FULL_REFRESH_TIMEZONE cannot be empty.")
return configured_value
def get_public_ranking_refresh_interval_seconds() -> int:
"""Return how often weekly/monthly public ranking snapshots should refresh."""
return _read_int_env(
"HLL_PUBLIC_RANKING_REFRESH_INTERVAL_SECONDS",
str(DEFAULT_PUBLIC_RANKING_REFRESH_INTERVAL_SECONDS),
minimum=1,
)
def get_public_recent_matches_refresh_interval_seconds() -> int:
"""Return how often recent-match public snapshots should refresh without event hooks."""
return _read_int_env(
"HLL_PUBLIC_RECENT_MATCHES_REFRESH_INTERVAL_SECONDS",
str(DEFAULT_PUBLIC_RECENT_MATCHES_REFRESH_INTERVAL_SECONDS),
minimum=1,
)
def get_rcon_capture_mode() -> str:
"""Return whether the worker runs the normal historical pipeline or live capture only."""
configured_mode = os.getenv("HLL_RCON_CAPTURE_MODE")

View File

@@ -6,8 +6,9 @@ import argparse
import json
import time
import traceback
from datetime import datetime, timezone
from datetime import date, datetime, time as datetime_time, timedelta, timezone
from typing import Any
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from .config import (
DEFAULT_DB_MAINTENANCE_INTERVAL_SECONDS,
@@ -20,6 +21,11 @@ from .config import (
get_historical_refresh_max_retries,
get_historical_refresh_retry_delay_seconds,
get_historical_data_source_kind,
get_public_full_refresh_enabled,
get_public_full_refresh_time,
get_public_full_refresh_timezone,
get_public_ranking_refresh_interval_seconds,
get_public_recent_matches_refresh_interval_seconds,
)
from .database_maintenance import run_database_maintenance_cleanup
from .elo_mmr_engine import rebuild_elo_mmr_models
@@ -28,8 +34,15 @@ from .historical_ingestion import run_incremental_refresh
from .historical_snapshots import (
generate_and_persist_historical_snapshots,
generate_and_persist_priority_historical_snapshots,
generate_and_persist_recent_historical_snapshots,
)
from .historical_storage import ALL_SERVERS_SLUG
from .rcon_annual_rankings import (
SUPPORTED_ANNUAL_RANKING_METRICS,
generate_annual_ranking_snapshot,
)
from .rcon_historical_leaderboards import refresh_ranking_snapshots
from .rcon_historical_leaderboards import SNAPSHOT_GENERATOR_SERVER_KEYS
from .rcon_historical_player_stats import (
refresh_player_period_stats,
refresh_player_search_index,
@@ -44,6 +57,14 @@ DEFAULT_HISTORICAL_SERVER_SCOPE = (
"comunidad-hispana-02",
)
_LAST_DATABASE_MAINTENANCE_RUN_AT: datetime | None = None
_LAST_PUBLIC_FULL_REFRESH_LOCAL_DATE: date | None = None
_LAST_PUBLIC_RANKING_REFRESH_AT: datetime | None = None
_LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT: datetime | None = None
_PUBLIC_REFRESH_IN_PROGRESS: set[str] = set()
PUBLIC_FULL_REFRESH_YEAR = 2026
PUBLIC_REFRESH_LOCK_FULL = "public-full-refresh"
PUBLIC_REFRESH_LOCK_RANKING = "public-ranking-refresh"
PUBLIC_REFRESH_LOCK_RECENT_MATCHES = "public-recent-matches-refresh"
def run_periodic_historical_refresh(
@@ -73,23 +94,41 @@ def run_periodic_historical_refresh(
)
print("Press Ctrl+C to stop.")
last_historical_refresh_started_at: datetime | None = None
loop_tick_seconds = _resolve_runner_tick_seconds(interval_seconds)
try:
while max_runs is None or completed_runs < max_runs:
completed_runs += 1
payload = _run_refresh_with_retries(
max_retries=max_retries,
retry_delay_seconds=retry_delay_seconds,
server_slug=server_slug,
max_pages=max_pages,
page_size=page_size,
run_number=completed_runs,
now = datetime.now(timezone.utc)
historical_refresh_due = _is_interval_due(
last_run_at=last_historical_refresh_started_at,
interval_seconds=interval_seconds,
now=now,
)
_emit_json_log({"run": completed_runs, **payload})
if historical_refresh_due:
completed_runs += 1
last_historical_refresh_started_at = now
payload = _run_refresh_with_retries(
max_retries=max_retries,
retry_delay_seconds=retry_delay_seconds,
server_slug=server_slug,
max_pages=max_pages,
page_size=page_size,
run_number=completed_runs,
)
_record_public_refreshes_from_cycle(payload, now=now)
_emit_json_log({"run": completed_runs, **payload})
else:
payload = _maybe_run_public_read_model_refreshes(
run_number=completed_runs,
now=now,
)
if payload["status"] != "skipped":
_emit_json_log({"run": completed_runs, **payload})
if max_runs is not None and completed_runs >= max_runs:
break
time.sleep(interval_seconds)
time.sleep(loop_tick_seconds)
except KeyboardInterrupt:
print("\nHistorical refresh loop stopped by user.")
@@ -193,6 +232,11 @@ def _run_refresh_with_retries(
server_slug=server_slug,
run_number=run_number,
)
recent_matches_event_result = _maybe_refresh_recent_matches_after_capture(
rcon_capture_result=rcon_capture_result,
server_slug=server_slug,
run_number=run_number,
)
maintenance_result = _maybe_run_database_maintenance()
return {
"status": _resolve_refresh_cycle_status(
@@ -201,6 +245,7 @@ def _run_refresh_with_retries(
player_search_index_result=player_search_index_result,
player_period_stats_result=player_period_stats_result,
ranking_snapshot_result=ranking_snapshot_result,
recent_matches_event_result=recent_matches_event_result,
elo_mmr_result=elo_mmr_result,
database_maintenance_result=maintenance_result,
),
@@ -215,6 +260,7 @@ def _run_refresh_with_retries(
"player_search_index_result": player_search_index_result,
"player_period_stats_result": player_period_stats_result,
"ranking_snapshot_result": ranking_snapshot_result,
"recent_matches_event_result": recent_matches_event_result,
"elo_mmr_result": elo_mmr_result,
"database_maintenance_result": maintenance_result,
}
@@ -415,6 +461,456 @@ def refresh_periodic_player_period_stats(
}
def _maybe_run_public_read_model_refreshes(
*,
run_number: int,
now: datetime | None = None,
) -> dict[str, Any]:
"""Run due public read-model refreshes without doing the heavy RCON capture cycle."""
anchor = _as_utc(now or datetime.now(timezone.utc))
results: dict[str, Any] = {}
if _is_public_full_refresh_due(anchor):
results["public_full_refresh_result"] = _run_non_overlapping_public_refresh(
PUBLIC_REFRESH_LOCK_FULL,
lambda: refresh_public_full_read_models(run_number=run_number, now=anchor),
)
if _is_public_interval_refresh_due(
last_run_at=_LAST_PUBLIC_RANKING_REFRESH_AT,
interval_seconds=get_public_ranking_refresh_interval_seconds(),
now=anchor,
):
results["ranking_snapshot_result"] = _run_non_overlapping_public_refresh(
PUBLIC_REFRESH_LOCK_RANKING,
lambda: refresh_public_ranking_snapshots(run_number=run_number, now=anchor),
)
if _is_public_interval_refresh_due(
last_run_at=_LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT,
interval_seconds=get_public_recent_matches_refresh_interval_seconds(),
now=anchor,
):
results["recent_matches_snapshot_result"] = _run_non_overlapping_public_refresh(
PUBLIC_REFRESH_LOCK_RECENT_MATCHES,
lambda: refresh_public_recent_matches_snapshots(
run_number=run_number,
now=anchor,
trigger="polling-interval",
),
)
if not results:
return {
"event": "public-read-model-refresh-scheduler-skipped",
"status": "skipped",
"reason": "no-public-refresh-due",
}
return {
"event": "public-read-model-refresh-scheduler-completed",
"status": _resolve_refresh_cycle_status(**results),
**results,
}
def refresh_public_full_read_models(
*,
run_number: int,
now: datetime | None = None,
) -> dict[str, Any]:
"""Refresh the full public read-model set intended for the low-load nightly window."""
global _LAST_PUBLIC_FULL_REFRESH_LOCAL_DATE
anchor = _as_utc(now or datetime.now(timezone.utc))
local_anchor = anchor.astimezone(_get_public_refresh_zone())
steps: dict[str, Any] = {}
started = time.perf_counter()
_emit_json_log(
{
"event": "public-full-refresh-started",
"run_number": run_number,
"scheduled_local_date": local_anchor.date().isoformat(),
"scheduled_time": get_public_full_refresh_time(),
"timezone": get_public_full_refresh_timezone(),
}
)
steps["historical_snapshots"] = _run_public_refresh_step(
"historical-snapshots-full",
lambda: generate_and_persist_historical_snapshots(generated_at=anchor),
)
steps["ranking_snapshots"] = _run_public_refresh_step(
"ranking-snapshots-weekly-monthly",
lambda: refresh_periodic_ranking_snapshots(run_number=run_number),
)
steps["annual_ranking_snapshots"] = _run_public_refresh_step(
"annual-ranking-snapshots-2026",
lambda: refresh_public_annual_ranking_snapshots(year=PUBLIC_FULL_REFRESH_YEAR),
)
steps["player_search_index"] = _run_public_refresh_step(
"player-search-index",
refresh_player_search_index,
)
steps["player_period_stats"] = _run_public_refresh_step(
"player-period-stats",
refresh_player_period_stats,
)
status = _resolve_refresh_cycle_status(**steps)
if status in {"ok", "partial"}:
_LAST_PUBLIC_FULL_REFRESH_LOCAL_DATE = local_anchor.date()
result = {
"status": status,
"run_number": run_number,
"generated_at": _to_iso(anchor),
"duration_ms": _elapsed_ms(started),
"timezone": get_public_full_refresh_timezone(),
"scheduled_time": get_public_full_refresh_time(),
"steps": steps,
}
_emit_json_log({"event": "public-full-refresh-completed", **result})
return result
def refresh_public_ranking_snapshots(
*,
run_number: int,
now: datetime | None = None,
) -> dict[str, Any]:
"""Refresh weekly/monthly public ranking snapshots on the short cadence."""
global _LAST_PUBLIC_RANKING_REFRESH_AT
anchor = _as_utc(now or datetime.now(timezone.utc))
started = time.perf_counter()
_emit_json_log(
{
"event": "public-ranking-refresh-started",
"run_number": run_number,
"interval_seconds": get_public_ranking_refresh_interval_seconds(),
}
)
result = refresh_periodic_ranking_snapshots(run_number=run_number)
status = str(result.get("status") or "ok")
if status != "error":
_LAST_PUBLIC_RANKING_REFRESH_AT = anchor
completed = {
**result,
"duration_ms": _elapsed_ms(started),
"generated_at": _to_iso(anchor),
"interval_seconds": get_public_ranking_refresh_interval_seconds(),
"generation_policy": "public-ranking-short-cadence",
}
_emit_json_log({"event": "public-ranking-refresh-completed", **completed})
return completed
def refresh_public_recent_matches_snapshots(
*,
run_number: int,
now: datetime | None = None,
trigger: str,
server_slug: str | None = None,
) -> dict[str, Any]:
"""Refresh recent-match snapshots after match-finalization evidence or short polling."""
global _LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT
anchor = _as_utc(now or datetime.now(timezone.utc))
started = time.perf_counter()
_emit_json_log(
{
"event": "public-recent-matches-refresh-started",
"run_number": run_number,
"trigger": trigger,
"server_slug": server_slug,
"interval_seconds": get_public_recent_matches_refresh_interval_seconds(),
}
)
try:
result = generate_and_persist_recent_historical_snapshots(
server_key=server_slug,
generated_at=anchor,
)
except Exception as exc: # noqa: BLE001 - scheduler must keep the runner alive
result = {
"status": "error",
"error_type": type(exc).__name__,
"error": str(exc),
"traceback": traceback.format_exc(),
}
status = str(result.get("status") or "ok")
if status != "error":
_LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT = anchor
completed = {
**result,
"duration_ms": _elapsed_ms(started),
"generated_at": _to_iso(anchor),
"trigger": trigger,
"server_slug": server_slug,
"interval_seconds": get_public_recent_matches_refresh_interval_seconds(),
}
_emit_json_log({"event": "public-recent-matches-refresh-completed", **completed})
return completed
def refresh_public_annual_ranking_snapshots(
*,
year: int = PUBLIC_FULL_REFRESH_YEAR,
limit: int = 20,
) -> dict[str, Any]:
"""Refresh supported annual ranking snapshots for all public scopes."""
combinations = [
(server_key, metric)
for server_key in SNAPSHOT_GENERATOR_SERVER_KEYS
for metric in SUPPORTED_ANNUAL_RANKING_METRICS
]
results: list[dict[str, Any]] = []
succeeded = 0
failed = 0
for server_key, metric in combinations:
try:
payload = generate_annual_ranking_snapshot(
year=year,
server_key=None if server_key == ALL_SERVERS_SLUG else server_key,
metric=metric,
limit=limit,
replace_existing=True,
)
snapshot = payload.get("snapshot") if isinstance(payload, dict) else {}
succeeded += 1
results.append(
{
"status": "ok",
"year": year,
"server_key": server_key,
"metric": metric,
"snapshot_id": snapshot.get("id") if isinstance(snapshot, dict) else None,
"ranked_players": int(payload.get("ranked_players") or 0),
"source_matches_count": int(payload.get("source_matches_count") or 0),
}
)
except Exception as exc: # noqa: BLE001 - report per-snapshot failures
failed += 1
results.append(
{
"status": "error",
"year": year,
"server_key": server_key,
"metric": metric,
"error_type": type(exc).__name__,
"error": str(exc),
}
)
status = "ok"
if failed and succeeded:
status = "partial"
elif failed:
status = "error"
return {
"status": status,
"year": year,
"limit": limit,
"combinations_expected": len(combinations),
"totals": {
"succeeded": succeeded,
"failed": failed,
},
"results": results,
}
def get_next_public_full_refresh_at(
*,
now: datetime | None = None,
) -> datetime:
"""Return the next configured daily public full refresh in UTC."""
anchor = _as_utc(now or datetime.now(timezone.utc))
zone = _get_public_refresh_zone()
local_anchor = anchor.astimezone(zone)
hour, minute = (int(part) for part in get_public_full_refresh_time().split(":"))
candidate = datetime.combine(
local_anchor.date(),
datetime_time(hour=hour, minute=minute),
tzinfo=zone,
)
if local_anchor >= candidate:
candidate += timedelta(days=1)
return candidate.astimezone(timezone.utc)
def _maybe_refresh_recent_matches_after_capture(
*,
rcon_capture_result: dict[str, Any],
server_slug: str | None,
run_number: int,
) -> dict[str, Any]:
if not _rcon_capture_materialized_finished_match(rcon_capture_result):
return {
"status": "skipped",
"reason": "no-materialized-finished-match-detected",
"trigger": "rcon-capture",
}
return _run_non_overlapping_public_refresh(
PUBLIC_REFRESH_LOCK_RECENT_MATCHES,
lambda: refresh_public_recent_matches_snapshots(
run_number=run_number,
trigger="rcon-capture-materialized-match",
server_slug=server_slug,
),
)
def _run_public_refresh_step(
step_name: str,
callback: Any,
) -> dict[str, Any]:
started = time.perf_counter()
_emit_json_log({"event": "public-refresh-step-started", "step": step_name})
try:
result = callback()
except Exception as exc: # noqa: BLE001 - keep neighboring refreshes running
result = {
"status": "error",
"error_type": type(exc).__name__,
"error": str(exc),
"traceback": traceback.format_exc(),
}
completed = {
**result,
"step": step_name,
"duration_ms": _elapsed_ms(started),
}
_emit_json_log({"event": "public-refresh-step-completed", **completed})
return completed
def _run_non_overlapping_public_refresh(
refresh_key: str,
callback: Any,
) -> dict[str, Any]:
if refresh_key in _PUBLIC_REFRESH_IN_PROGRESS:
return {
"status": "skipped",
"reason": "refresh-already-in-progress",
"refresh_key": refresh_key,
}
_PUBLIC_REFRESH_IN_PROGRESS.add(refresh_key)
try:
return callback()
finally:
_PUBLIC_REFRESH_IN_PROGRESS.discard(refresh_key)
def _record_public_refreshes_from_cycle(
payload: dict[str, Any],
*,
now: datetime,
) -> None:
global _LAST_PUBLIC_RANKING_REFRESH_AT, _LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT
ranking_result = payload.get("ranking_snapshot_result")
if isinstance(ranking_result, dict) and ranking_result.get("status") != "error":
_LAST_PUBLIC_RANKING_REFRESH_AT = now
recent_result = payload.get("recent_matches_event_result")
if isinstance(recent_result, dict) and recent_result.get("status") not in {
"error",
"skipped",
}:
_LAST_PUBLIC_RECENT_MATCHES_REFRESH_AT = now
def _is_public_full_refresh_due(now: datetime) -> bool:
if not get_public_full_refresh_enabled():
return False
local_now = now.astimezone(_get_public_refresh_zone())
hour, minute = (int(part) for part in get_public_full_refresh_time().split(":"))
scheduled_today = datetime.combine(
local_now.date(),
datetime_time(hour=hour, minute=minute),
tzinfo=local_now.tzinfo,
)
return (
local_now >= scheduled_today
and _LAST_PUBLIC_FULL_REFRESH_LOCAL_DATE != local_now.date()
)
def _is_public_interval_refresh_due(
*,
last_run_at: datetime | None,
interval_seconds: int,
now: datetime,
) -> bool:
return _is_interval_due(
last_run_at=last_run_at,
interval_seconds=interval_seconds,
now=now,
)
def _is_interval_due(
*,
last_run_at: datetime | None,
interval_seconds: int,
now: datetime,
) -> bool:
if last_run_at is None:
return True
elapsed_seconds = (now - _as_utc(last_run_at)).total_seconds()
return elapsed_seconds >= interval_seconds
def _resolve_runner_tick_seconds(interval_seconds: int) -> int:
intervals = [
interval_seconds,
get_public_ranking_refresh_interval_seconds(),
get_public_recent_matches_refresh_interval_seconds(),
]
return max(1, min(intervals))
def _get_public_refresh_zone() -> ZoneInfo:
timezone_name = get_public_full_refresh_timezone()
try:
return ZoneInfo(timezone_name)
except ZoneInfoNotFoundError as error:
raise ValueError(
f"HLL_PUBLIC_FULL_REFRESH_TIMEZONE is not a valid IANA timezone: {timezone_name}"
) from error
def _rcon_capture_materialized_finished_match(
rcon_capture_result: dict[str, Any],
) -> bool:
totals = rcon_capture_result.get("totals")
if isinstance(totals, dict) and int(totals.get("materialized_matches_inserted") or 0) > 0:
return True
targets = rcon_capture_result.get("targets")
if not isinstance(targets, list):
return False
for target in targets:
if not isinstance(target, dict):
continue
if int(target.get("materialized_matches_inserted") or 0) > 0:
return True
return False
def _elapsed_ms(started: float) -> int:
return int((time.perf_counter() - started) * 1000)
def _as_utc(value: datetime) -> datetime:
if value.tzinfo is None:
return value.replace(tzinfo=timezone.utc)
return value.astimezone(timezone.utc)
def _to_iso(value: datetime) -> str:
return _as_utc(value).isoformat().replace("+00:00", "Z")
def _resolve_refresh_cycle_status(**results: dict[str, Any]) -> str:
statuses = [
str(result.get("status") or "").strip().lower()

View File

@@ -392,6 +392,49 @@ def generate_and_persist_priority_historical_snapshots(
}
def generate_and_persist_recent_historical_snapshots(
*,
server_key: str | None = None,
generated_at: datetime | None = None,
recent_matches_limit: int = DEFAULT_RECENT_MATCHES_LIMIT,
db_path: Path | None = None,
) -> dict[str, object]:
"""Build and persist only the public recent-matches snapshots."""
from .historical_snapshot_storage import persist_historical_snapshot_batch
generated_at_value = _as_utc(generated_at or datetime.now(timezone.utc))
recent_matches_limit = _normalize_snapshot_limit(
"recent_matches_limit",
recent_matches_limit,
)
snapshots = [
_build_recent_matches_snapshot(
target_server_key,
generated_at_value,
limit=recent_matches_limit,
db_path=db_path,
)
for target_server_key in _resolve_snapshot_target_keys(
server_key=server_key,
db_path=db_path,
)
]
persisted_records = persist_historical_snapshot_batch(snapshots, db_path=db_path)
snapshots_by_server: dict[str, int] = {}
for record in persisted_records:
snapshots_by_server.setdefault(record.server_key, 0)
snapshots_by_server[record.server_key] += 1
return {
"generated_at": _to_iso(generated_at_value),
"server_slug": server_key,
"snapshot_policy": "recent-matches-only",
"snapshot_count": len(persisted_records),
"servers_processed": len(snapshots_by_server),
"snapshots_by_server": snapshots_by_server,
}
def _build_server_summary_snapshot(
server_key: str,
generated_at: datetime,

View File

@@ -1338,17 +1338,6 @@ def build_leaderboard_snapshot_payload(
payload = snapshot.get("payload") if snapshot else {}
items = payload.get("items") if isinstance(payload, dict) else None
sliced_items = list(items[:limit]) if isinstance(items, list) else []
runtime_enrichment_applied = False
if _leaderboard_snapshot_items_need_playtime_enrichment(sliced_items):
runtime_items = _load_runtime_leaderboard_items(
limit=limit,
server_id=server_id,
metric=metric,
timeframe=normalized_timeframe,
)
if runtime_items:
sliced_items = runtime_items[:limit]
runtime_enrichment_applied = True
is_all_servers = server_id == ALL_SERVERS_SLUG
return {
"status": "ok",
@@ -1391,12 +1380,8 @@ def build_leaderboard_snapshot_payload(
"snapshot_limit": payload.get("limit") if isinstance(payload, dict) else None,
"limit": limit,
"runtime_enrichment": {
"applied": runtime_enrichment_applied,
"reason": (
"snapshot-items-missing-total-time-seconds"
if runtime_enrichment_applied
else None
),
"applied": False,
"reason": "disabled-on-public-snapshot-path",
},
**_resolve_historical_fallback_policy(
fallback_reason="rcon-historical-read-model-does-not-support-historical-snapshots-yet",
@@ -1450,55 +1435,6 @@ def build_recent_historical_matches_snapshot_payload(
payload = snapshot.get("payload") if snapshot else {}
items = payload.get("items") if isinstance(payload, dict) else None
sliced_items = list(items[:limit]) if isinstance(items, list) else []
if (
get_historical_data_source_kind() == SOURCE_KIND_RCON
and 0 < len(sliced_items) < limit
):
fallback_items = list_recent_historical_matches(limit=limit, server_slug=server_slug)
merged_items = _merge_recent_match_items(
primary_items=sliced_items,
fallback_items=fallback_items,
limit=limit,
)
if len(merged_items) > len(sliced_items):
return {
"status": "ok",
"data": {
"title": "Snapshot historico de partidas recientes por servidor",
"context": "historical-recent-matches-snapshot",
"source": "historical-precomputed-snapshots",
"server_slug": server_slug,
"found": snapshot is not None,
**_build_historical_snapshot_metadata(snapshot),
"snapshot_limit": payload.get("limit") if isinstance(payload, dict) else None,
"limit": limit,
**build_source_policy(
primary_source=SOURCE_KIND_RCON,
selected_source="hybrid-rcon-plus-public-scoreboard",
fallback_used=True,
fallback_reason="rcon-historical-recent-matches-did-not-reach-requested-limit",
source_attempts=[
build_source_attempt(
source=SOURCE_KIND_RCON,
role="primary",
status="success",
reason="recent-matches-snapshot-served-by-rcon-competitive-model",
),
build_source_attempt(
source=SOURCE_KIND_PUBLIC_SCOREBOARD,
role="fallback",
status="success",
reason="recent-matches-snapshot-completed-from-public-scoreboard",
message=(
f"RCON snapshot returned {len(sliced_items)} items, completed to "
f"{len(merged_items)} of requested {limit}."
),
),
],
),
"items": merged_items,
},
}
return {
"status": "ok",
"data": {

View File

@@ -6,7 +6,6 @@ from http import HTTPStatus
from datetime import datetime, timezone
from urllib.parse import parse_qs, urlparse
from .config import get_historical_data_source_kind
from .payloads import (
build_global_ranking_payload,
build_stats_player_profile_payload,
@@ -46,7 +45,6 @@ from .payloads import (
build_weekly_top_kills_payload,
build_stats_player_search_payload,
)
from .rcon_historical_leaderboards import build_rcon_materialized_leaderboard_snapshot_payload
from .scoreboard_origins import get_trusted_public_scoreboard_origin
RANKING_METRICS = {
@@ -305,13 +303,6 @@ def resolve_get_payload(path: str) -> tuple[HTTPStatus | None, dict[str, object]
return HTTPStatus.BAD_REQUEST, build_error_payload("Invalid metric parameter")
if timeframe not in {"weekly", "monthly"}:
return HTTPStatus.BAD_REQUEST, build_error_payload("Invalid timeframe parameter")
if get_historical_data_source_kind() == "rcon":
return HTTPStatus.OK, build_rcon_materialized_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,
metric=metric,
timeframe=timeframe,
)
return HTTPStatus.OK, build_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,
@@ -328,13 +319,6 @@ def resolve_get_payload(path: str) -> tuple[HTTPStatus | None, dict[str, object]
metric = params.get("metric", ["kills"])[0]
if metric not in {"kills", "deaths", "support", "matches_over_100_kills"}:
return HTTPStatus.BAD_REQUEST, build_error_payload("Invalid metric parameter")
if get_historical_data_source_kind() == "rcon":
return HTTPStatus.OK, build_rcon_materialized_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,
metric=metric,
timeframe="monthly",
)
return HTTPStatus.OK, build_monthly_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,
@@ -385,13 +369,6 @@ def resolve_get_payload(path: str) -> tuple[HTTPStatus | None, dict[str, object]
metric = params.get("metric", ["kills"])[0]
if metric not in {"kills", "deaths", "support", "matches_over_100_kills"}:
return HTTPStatus.BAD_REQUEST, build_error_payload("Invalid metric parameter")
if get_historical_data_source_kind() == "rcon":
return HTTPStatus.OK, build_rcon_materialized_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,
metric=metric,
timeframe="weekly",
)
return HTTPStatus.OK, build_weekly_leaderboard_snapshot_payload(
limit=limit,
server_id=server_id,