Files
comunidadhll/ai/tasks/done/TASK-201-automate-player-read-model-refresh.md
2026-06-09 11:59:31 +02:00

9.8 KiB

id, title, status, type, team, supporting_teams, roadmap_item, priority
id title status type team supporting_teams roadmap_item priority
TASK-201-automate-player-read-model-refresh Automate player read model refresh done backend Backend Senior
Arquitecto de Base de Datos
Arquitecto Python
foundation high

TASK-201 - Automate player read model refresh

Goal

Automate the periodic refresh of player_search_index and player_period_stats inside the existing backend historical runner so the public stats endpoints can keep using their read models without depending on manual operator refreshes.

Context

Production already validates the full read-model chain required by the public player stats flows:

  • player_search_index exists and /api/stats/players/search already uses read_model=player-search-index with fallback_used=false
  • player_period_stats exists and /api/stats/players/{player_id} already uses read_model=player-period-stats with fallback_used=false
  • ranking_snapshots / ranking_snapshot_items already exist
  • refresh-ranking-snapshots --limit 30 already runs in the periodic historical runner
  • PostgreSQL is the operational storage target
  • runtime fallback must remain preserved for player stats and ranking

The remaining gap is operational automation for the two player read models. The existing periodic runner in backend/app/historical_runner.py is the intended scheduler surface, so this task must extend that cycle instead of inventing a new scheduler. The cycle should keep refresh-ranking-snapshots and add dedicated, separately reported refresh steps for player search and player period stats.

Preserve the current product identity: Spanish-speaking HLL Vietnam community, military/Vietnam/tactical/sober visual direction and controlled repository evolution.

Steps

  1. Read the listed files first.
  2. Inspect the current historical runner flow and the existing manual read-model refresh entrypoints.
  3. Integrate refresh_player_search_index(...) into the periodic backend cycle.
  4. Integrate refresh_player_period_stats(...) into the periodic backend cycle.
  5. Keep refresh-ranking-snapshots in the same cycle and preserve its current behavior.
  6. Run the cycle in this order unless the current runner architecture safely requires a different invariant:
    • existing RCON ingestion/materialization cycle
    • player_search_index
    • player_period_stats
    • ranking_snapshots
  7. Report player_search_index_result, player_period_stats_result and ranking_snapshot_result separately.
  8. Preserve visibility of failures and avoid hiding one read-model failure behind another. One failure should not necessarily prevent the remaining refresh attempts unless the runner already enforces a stricter global policy.
  9. Preserve the manual CLIs and the public contracts of:
    • /api/stats/players/search
    • /api/stats/players/{player_id}
    • /api/ranking
  10. Document the automatic runner refresh, inherited cadence, emergency manual commands, fallback preservation and final cycle order.

Files to Read First

  • AGENTS.md
  • ai/repo-context.md
  • ai/architecture-index.md
  • backend/app/historical_runner.py
  • backend/app/rcon_historical_player_stats.py
  • backend/app/rcon_historical_leaderboards.py
  • backend/app/postgres_rcon_storage.py
  • backend/app/payloads.py
  • docs/player-search-read-model-plan.md
  • docs/player-period-stats-read-model-plan.md
  • docs/ranking-snapshot-read-model-plan.md
  • scripts/run-stats-validation.ps1
  • ai/tasks/done/TASK-197-automate-ranking-snapshot-refresh.md
  • ai/tasks/done/TASK-198-add-player-search-read-model.md
  • ai/tasks/done/TASK-199-add-player-period-stats-read-model.md
  • ai/tasks/done/TASK-200-fix-player-profile-runtime-postgres-grouping.md

Expected Files to Modify

  • backend/app/historical_runner.py
  • docs/player-search-read-model-plan.md
  • docs/player-period-stats-read-model-plan.md
  • docs/ranking-snapshot-read-model-plan.md
  • scripts/run-stats-validation.ps1
  • ai/tasks/in-progress/TASK-201-automate-player-read-model-refresh.md

Constraints

  • Keep the change minimal and backend/documentation only.
  • Do not execute ai-platform run.
  • Do not modify frontend.
  • Do not change design.
  • Do not touch images or assets.
  • Do not reactivate Elo/MMR.
  • Do not reintroduce Comunidad Hispana #03.
  • Do not mix visual corrections into this task.
  • Preserve PostgreSQL as the operational storage target.
  • Preserve runtime fallback for player stats and ranking.
  • Do not break the manual CLIs:
    • refresh-player-search-index
    • refresh-player-period-stats
    • refresh-ranking-snapshots
    • generate-ranking-snapshot
  • Do not change public contracts of:
    • /api/stats/players/search
    • /api/stats/players/{player_id}
    • /api/ranking

Validation

Before completing the task ensure:

  • python -m py_compile backend/app/historical_runner.py backend/app/rcon_historical_player_stats.py
  • powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1
  • powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1
  • local/import validation proves the runner calls:
    • refresh_player_search_index(...)
    • refresh_player_period_stats(...)
    • refresh_ranking_snapshots(...)
  • validation proves the periodic cycle reports these results separately:
    • player_search_index_result
    • player_period_stats_result
    • ranking_snapshot_result
  • validation proves the manual CLIs still work:
    • refresh-player-search-index
    • refresh-player-period-stats
    • refresh-ranking-snapshots
  • validation proves /api/stats/players/search can still use player-search-index
  • validation proves /api/stats/players/{player_id} can still use player-period-stats
  • validation proves ranking snapshots are not broken
  • git diff --name-only matches the expected scope

Outcome

Implemented:

  • backend/app/historical_runner.py
    • integrated automatic periodic refresh of player_search_index
    • integrated automatic periodic refresh of player_period_stats
    • preserved the existing ranking_snapshots refresh
    • the runner now reports:
      • player_search_index_result
      • player_period_stats_result
      • ranking_snapshot_result
    • one read-model failure is now reported explicitly and does not prevent the runner from attempting the remaining refresh steps in the same cycle
    • the runner returns status=partial when one of those periodic read-model refresh steps fails but the cycle continues
  • docs/player-search-read-model-plan.md
    • documents automatic runner refresh
    • documents inherited cadence from HLL_HISTORICAL_REFRESH_INTERVAL_SECONDS
    • documents the emergency manual command
    • documents fallback preservation
  • docs/player-period-stats-read-model-plan.md
    • documents automatic runner refresh
    • documents inherited cadence from HLL_HISTORICAL_REFRESH_INTERVAL_SECONDS
    • documents the emergency manual command
    • documents fallback preservation
  • docs/ranking-snapshot-read-model-plan.md
    • documents the final runner order
    • documents that ranking refresh remains part of the same periodic cycle
    • documents cadence inheritance and fallback preservation
  • scripts/run-stats-validation.ps1
    • validates that the runner calls the player read-model refreshes before ranking snapshots
    • validates separate result reporting
    • validates that a player_search_index refresh failure remains visible and does not stop the remaining periodic refresh attempts

Modified files:

  • backend/app/historical_runner.py
  • docs/player-search-read-model-plan.md
  • docs/player-period-stats-read-model-plan.md
  • docs/ranking-snapshot-read-model-plan.md
  • scripts/run-stats-validation.ps1
  • ai/tasks/done/TASK-201-automate-player-read-model-refresh.md

Final runner order:

  1. existing RCON ingestion/materialization cycle
  2. player_search_index
  3. player_period_stats
  4. ranking_snapshots

Added cycle results/logs:

  • player_search_index_result
  • player_period_stats_result
  • ranking_snapshot_result
  • start events:
    • player-search-index-refresh-started
    • player-period-stats-refresh-started
  • failure events:
    • player-search-index-refresh-failed
    • player-period-stats-refresh-failed

Validations executed:

  • python -m py_compile backend/app/historical_runner.py backend/app/rcon_historical_player_stats.py
  • powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1
  • powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1

Production validation path:

  • run one controlled backend cycle:
    • python -m app.historical_runner --max-runs 1
  • verify the cycle output includes:
    • player_search_index_result
    • player_period_stats_result
    • ranking_snapshot_result
  • confirm the player stats endpoints still report:
    • /api/stats/players/search -> source.read_model=player-search-index
    • /api/stats/players/{player_id} -> source.read_model=player-period-stats
  • confirm /api/ranking still serves ranking snapshots when ready and preserves fallback behavior when a snapshot is missing
  • if emergency rebuild is needed, operators can still run:
    • python -m app.rcon_historical_player_stats refresh-player-search-index
    • python -m app.rcon_historical_player_stats refresh-player-period-stats
    • python -m app.rcon_historical_leaderboards refresh-ranking-snapshots --limit 30

Pending limitations kept out of scope:

  • the runner still refreshes the player read models for all supported public scopes even when a manual runner execution is limited with --server
  • this task does not introduce a separate external scheduler or deployment-specific cron wiring
  • live HTTP validation at http://127.0.0.1:8000 was not available in this environment; route-contract validation passed via local Python imports instead

Change Budget

  • Prefer fewer than 5 modified files.
  • Prefer changes under 200 lines when feasible.
  • Split the work into follow-up tasks if limits are exceeded.