Blend recent matches rcon fallback

This commit is contained in:
devRaGonSa
2026-03-26 11:10:10 +01:00
parent 3e9f8dce5f
commit d1d8bcf8b8

View File

@@ -367,9 +367,78 @@ def build_recent_historical_matches_payload(
if get_historical_data_source_kind() == "rcon":
data_source = get_rcon_historical_read_model()
if data_source is not None:
items = data_source.list_recent_activity(server_key=server_slug, limit=limit)
capabilities = data_source.describe_capabilities()
if items:
try:
items = data_source.list_recent_activity(server_key=server_slug, limit=limit)
except Exception as error: # noqa: BLE001 - explicit runtime fallback boundary
items = []
rcon_source_policy = build_historical_runtime_source_policy(
operation="historical-recent-matches",
rcon_status="error",
fallback_reason="rcon-historical-read-model-request-failed",
rcon_message=str(error),
)
else:
rcon_source_policy = build_historical_runtime_source_policy(
operation="historical-recent-matches",
rcon_status=(
"success"
if data_source.has_recent_activity_coverage(items)
else "empty"
),
fallback_reason="rcon-historical-read-model-has-no-recent-activity",
)
if not bool(rcon_source_policy.get("fallback_used")):
if 0 < len(items) < limit:
fallback_items = list_recent_historical_matches(limit=limit, server_slug=server_slug)
merged_items = _merge_recent_match_items(
primary_items=items,
fallback_items=fallback_items,
limit=limit,
)
if len(merged_items) > len(items):
return {
"status": "ok",
"data": {
"title": "Actividad competitiva reciente capturada por RCON",
"context": "historical-recent-matches",
"source": "hybrid-rcon-plus-public-scoreboard",
"historical_data_source": "rcon",
"supported": True,
"coverage_basis": "rcon-competitive-windows-plus-public-scoreboard-fallback",
"limit": limit,
"server_slug": server_slug,
**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="historical-recent-matches-served-by-rcon",
),
build_source_attempt(
source=SOURCE_KIND_PUBLIC_SCOREBOARD,
role="fallback",
status="success",
reason="historical-recent-matches-completed-from-public-scoreboard",
message=(
f"RCON returned {len(items)} items, completed to "
f"{len(merged_items)} of requested {limit}."
),
),
],
),
"items": merged_items,
"capabilities": capabilities,
},
}
return {
"status": "ok",
"data": {
@@ -381,17 +450,7 @@ def build_recent_historical_matches_payload(
"coverage_basis": "rcon-competitive-windows",
"limit": limit,
"server_slug": server_slug,
**build_source_policy(
primary_source=SOURCE_KIND_RCON,
selected_source=SOURCE_KIND_RCON,
source_attempts=[
build_source_attempt(
source=SOURCE_KIND_RCON,
role="primary",
status="success",
)
],
),
**rcon_source_policy,
"items": items,
"capabilities": capabilities,
},
@@ -405,8 +464,13 @@ def build_recent_historical_matches_payload(
"source": "historical-crcon-storage",
"limit": limit,
"server_slug": server_slug,
**_resolve_historical_fallback_policy(
fallback_reason="rcon-historical-read-model-has-no-recent-activity",
**(
rcon_source_policy
if get_historical_data_source_kind() == "rcon"
and "rcon_source_policy" in locals()
else _resolve_historical_fallback_policy(
fallback_reason="rcon-historical-read-model-has-no-recent-activity",
)
),
"items": items,
},
@@ -665,6 +729,55 @@ 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": {
@@ -1277,6 +1390,52 @@ def _resolve_historical_fallback_policy(
)
def _merge_recent_match_items(
*,
primary_items: list[dict[str, object]],
fallback_items: list[dict[str, object]],
limit: int,
) -> list[dict[str, object]]:
merged: list[dict[str, object]] = []
seen_keys: set[str] = set()
for item in list(primary_items) + list(fallback_items):
if not isinstance(item, dict):
continue
dedupe_key = _build_recent_match_dedupe_key(item)
if dedupe_key in seen_keys:
continue
seen_keys.add(dedupe_key)
merged.append(item)
merged.sort(key=_recent_match_sort_key, reverse=True)
return merged[:limit]
def _build_recent_match_dedupe_key(item: dict[str, object]) -> str:
server = item.get("server") if isinstance(item.get("server"), dict) else {}
map_payload = item.get("map") if isinstance(item.get("map"), dict) else {}
match_id = str(item.get("match_id") or "").strip()
server_slug = str(server.get("slug") or server.get("external_server_id") or "").strip()
map_name = str(map_payload.get("name") or map_payload.get("pretty_name") or "").strip().lower()
closed_at = _truncate_recent_match_timestamp(
item.get("closed_at") or item.get("ended_at")
)
started_at = _truncate_recent_match_timestamp(item.get("started_at"))
if match_id and match_id.isdigit():
return f"scoreboard:{server_slug}:{match_id}"
return f"recent:{server_slug}:{map_name}:{started_at}:{closed_at}"
def _truncate_recent_match_timestamp(value: object) -> str:
normalized = str(value or "").strip()
return normalized[:16] if normalized else ""
def _recent_match_sort_key(item: dict[str, object]) -> tuple[str, str]:
closed_at = str(item.get("closed_at") or item.get("ended_at") or "").strip()
started_at = str(item.get("started_at") or "").strip()
return (closed_at, started_at)
def _infer_live_source_policy_from_items(
items: list[dict[str, object]],
*,