224 lines
9.8 KiB
Markdown
224 lines
9.8 KiB
Markdown
---
|
|
id: TASK-201-automate-player-read-model-refresh
|
|
title: Automate player read model refresh
|
|
status: done
|
|
type: backend
|
|
team: Backend Senior
|
|
supporting_teams:
|
|
- Arquitecto de Base de Datos
|
|
- Arquitecto Python
|
|
roadmap_item: foundation
|
|
priority: 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.
|