Automate player read model refresh
This commit is contained in:
223
ai/tasks/done/TASK-201-automate-player-read-model-refresh.md
Normal file
223
ai/tasks/done/TASK-201-automate-player-read-model-refresh.md
Normal file
@@ -0,0 +1,223 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user