Add ranking snapshot performance path
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
---
|
||||
id: TASK-188-audit-ranking-and-stats-query-performance
|
||||
title: Audit ranking and stats query performance
|
||||
status: done
|
||||
type: research
|
||||
team: Arquitecto de Base de Datos
|
||||
supporting_teams:
|
||||
- Backend Senior
|
||||
- Arquitecto Python
|
||||
roadmap_item: foundation
|
||||
priority: high
|
||||
---
|
||||
|
||||
# TASK-188 - Audit ranking and stats query performance
|
||||
|
||||
## Goal
|
||||
|
||||
Measure and document the real performance of the public Ranking and Stats endpoints before applying any optimization.
|
||||
|
||||
## Context
|
||||
|
||||
`/api/ranking` weekly/monthly currently reads runtime aggregates over materialized RCON/AdminLog tables, while annual ranking already uses snapshots. `Stats` search and player detail also depend on runtime reads over the same materialized domain. HLL Vietnam needs a concrete baseline for latency, query shape, table size and execution plan so follow-up work can target the real bottlenecks instead of guessing.
|
||||
|
||||
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. Identify the exact queries and tables used by the target endpoints.
|
||||
3. Measure request time and, if possible, approximate SQL time for each target endpoint.
|
||||
4. Inspect relevant row counts, current indexes and execution plans when the engine allows it.
|
||||
5. Document the slowest endpoint and provide a concrete index/snapshot recommendation without changing runtime code.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `backend/app/routes.py`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/rcon_historical_player_stats.py`
|
||||
- `backend/app/rcon_annual_rankings.py`
|
||||
- `docs/global-ranking-page-plan.md`
|
||||
- `scripts/run-stats-validation.ps1`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `docs/ranking-stats-performance-audit.md`
|
||||
- `ai/tasks/done/TASK-188-audit-ranking-and-stats-query-performance.md`
|
||||
|
||||
## Constraints
|
||||
|
||||
- No modificar lógica de backend.
|
||||
- No modificar frontend.
|
||||
- No crear migraciones.
|
||||
- No crear índices todavía.
|
||||
- No cambiar APIs.
|
||||
- No reactivar Elo/MMR.
|
||||
- No reintroducir Comunidad Hispana #03.
|
||||
- Mantener el trabajo limitado a medición y documentación.
|
||||
|
||||
## Validation
|
||||
|
||||
Before completing the task ensure:
|
||||
|
||||
- the audit covers:
|
||||
- `/api/ranking` weekly `kills`
|
||||
- `/api/ranking` weekly `kd_ratio`
|
||||
- `/api/ranking` monthly `kills_per_match`
|
||||
- `/api/ranking` annual `kills`
|
||||
- `/api/stats/players/search`
|
||||
- `/api/stats/players/{player_id}`
|
||||
- the document records:
|
||||
- total request time
|
||||
- approximate SQL time if measurable
|
||||
- row counts for relevant tables
|
||||
- existing indexes
|
||||
- current query shapes
|
||||
- execution plan if available
|
||||
- slowest endpoint
|
||||
- concrete recommendation for indexes and/or snapshots
|
||||
- executed commands are documented
|
||||
- inability to obtain `EXPLAIN` is documented if the environment blocks it
|
||||
- `git diff --name-only` stays within scope
|
||||
|
||||
## Outcome
|
||||
|
||||
- Audit completed in `docs/ranking-stats-performance-audit.md`.
|
||||
- Measured baseline captured for the six required endpoint probes.
|
||||
- Environment limitation documented: backend HTTP server was not running locally, so request timing used in-process route resolution and SQL tracing over SQLite.
|
||||
- Current runtime weekly/monthly ranking windows on `2026-06-09` are empty because the latest materialized `admin-log-match-ended` data ends on `2026-05-20T23:21:45.816Z`.
|
||||
- Slowest measured endpoint is `/api/stats/players/{player_id}` because it issues 16 SQL statements, including repeated window counts and two ranking-position subqueries.
|
||||
- Primary future performance risks identified:
|
||||
- full scans on `rcon_materialized_matches` for `source_basis + time-window` filters
|
||||
- a full scan on `rcon_match_player_stats` for player-detail-by-`player_id`
|
||||
- temp B-trees for grouping, `COUNT(DISTINCT)` and ordering in leaderboard/search queries
|
||||
- Follow-up recommendation for `TASK-189`:
|
||||
- add time-window indexes on `rcon_materialized_matches`
|
||||
- add a direct `player_id` index on `rcon_match_player_stats`
|
||||
- keep annual snapshot indexes as-is
|
||||
- Follow-up recommendation for `TASK-190`:
|
||||
- move weekly/monthly public ranking to snapshot-backed reads with controlled runtime fallback
|
||||
|
||||
## 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.
|
||||
106
ai/tasks/done/TASK-189-add-ranking-materialized-read-indexes.md
Normal file
106
ai/tasks/done/TASK-189-add-ranking-materialized-read-indexes.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
id: TASK-189-add-ranking-materialized-read-indexes
|
||||
title: Add ranking materialized read indexes
|
||||
status: done
|
||||
type: backend
|
||||
team: Arquitecto de Base de Datos
|
||||
supporting_teams:
|
||||
- Backend Senior
|
||||
roadmap_item: foundation
|
||||
priority: high
|
||||
---
|
||||
|
||||
# TASK-189 - Add ranking materialized read indexes
|
||||
|
||||
## Goal
|
||||
|
||||
Add safe indexes on the materialized tables used by Ranking and Stats to reduce scans and improve join performance.
|
||||
|
||||
## Context
|
||||
|
||||
The current Ranking and Stats runtime reads depend on `rcon_materialized_matches`, `rcon_match_player_stats` and annual snapshot tables. After `TASK-188` documents the real bottlenecks, HLL Vietnam needs a narrow indexing pass that improves read performance without changing API contracts, recalculating rankings or introducing a second architecture.
|
||||
|
||||
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. Use `docs/ranking-stats-performance-audit.md` as the primary justification source.
|
||||
3. Add only the indexes supported by the audit findings and the existing storage initialization/migration pattern.
|
||||
4. Keep SQLite/Postgres compatibility if both storage modes are supported in the current backend.
|
||||
5. Re-run the validation scripts and, if possible, compare before/after timing for one or two representative endpoints.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `docs/ranking-stats-performance-audit.md`
|
||||
- `backend/app/rcon_admin_log_materialization.py`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/rcon_historical_player_stats.py`
|
||||
- `backend/app/rcon_annual_rankings.py`
|
||||
- `backend/app/postgres_rcon_storage.py`
|
||||
- `backend/app/sqlite_utils.py`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `backend` storage/migration/init module correspondiente, según patrón existente
|
||||
- `docs/ranking-stats-performance-audit.md`, solo si se documenta índice aplicado
|
||||
- `ai/tasks/done/TASK-189-add-ranking-materialized-read-indexes.md`
|
||||
|
||||
## Constraints
|
||||
|
||||
- No cambiar contratos API.
|
||||
- No cambiar frontend.
|
||||
- No recalcular rankings.
|
||||
- No crear snapshots en esta task.
|
||||
- No reactivar Elo/MMR.
|
||||
- No reintroducir Comunidad Hispana #03.
|
||||
- Mantener compatibilidad SQLite/Postgres si el proyecto usa ambos.
|
||||
- Basar los índices en evidencia documentada por `TASK-188`, no en suposiciones.
|
||||
|
||||
## Validation
|
||||
|
||||
Before completing the task ensure:
|
||||
|
||||
- the chosen indexes are justified by `TASK-188`
|
||||
- candidate coverage explicitly considers:
|
||||
- `target_key + match_key`
|
||||
- `source_basis + ended_at/started_at`
|
||||
- `target_key + ended_at/started_at`
|
||||
- `player_id`
|
||||
- `player_name` if search benefits from it
|
||||
- `snapshot_id + ranking_position` in snapshot tables if applicable
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1`
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||
- if possible, before/after timing is captured for one or two endpoints
|
||||
- any measurement limitations are documented
|
||||
- `git diff --name-only` stays within scope
|
||||
|
||||
## Outcome
|
||||
|
||||
- Added SQLite/PostgreSQL-compatible indexes in the materialized storage initialization path:
|
||||
- `idx_rcon_materialized_matches_source_window_text`
|
||||
- `idx_rcon_materialized_matches_target_source_window_text`
|
||||
- `idx_rcon_materialized_matches_external_source_window_text`
|
||||
- `idx_rcon_match_player_stats_player_id_match`
|
||||
- Kept existing annual snapshot indexes unchanged because the annual read path was already using matching indexes.
|
||||
- Did not add a plain `player_name` B-tree because the current search query uses `LOWER(player_name) LIKE '%term%'` with a leading wildcard, so the audit did not justify it as a useful narrow index.
|
||||
- Post-index validation confirmed plan improvement:
|
||||
- weekly count queries now use the new `source_basis + window` covering index
|
||||
- the stats player-detail aggregate now narrows through the match window index and then probes stats by `(target_key, match_key, player_id)`
|
||||
- Before/after timing was captured for representative endpoints:
|
||||
- `/api/stats/players/{player_id}` weekly improved from `8.924 ms / 4.494 ms SQL` to `4.300 ms / 1.043 ms SQL`
|
||||
- `/api/ranking` weekly `kills` showed no meaningful change in this dataset because the active weekly window on `2026-06-09` is empty
|
||||
- Validation scripts executed successfully:
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1`
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||
- Remaining gap:
|
||||
- weekly/monthly public ranking still does repeated runtime counting and grouped aggregation per request, so snapshot-backed reads remain necessary in `TASK-190` and `TASK-191`
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
id: TASK-190-design-weekly-monthly-ranking-snapshots
|
||||
title: Design weekly monthly ranking snapshots
|
||||
status: done
|
||||
type: documentation
|
||||
team: Arquitecto de Base de Datos
|
||||
supporting_teams:
|
||||
- Backend Senior
|
||||
- PM
|
||||
roadmap_item: foundation
|
||||
priority: high
|
||||
---
|
||||
|
||||
# TASK-190 - Design weekly monthly ranking snapshots
|
||||
|
||||
## Goal
|
||||
|
||||
Design a weekly/monthly ranking snapshot read model, equivalent in philosophy to the annual snapshot model, so public ranking requests do not depend on expensive runtime aggregation.
|
||||
|
||||
## Context
|
||||
|
||||
Annual ranking already follows a snapshot-backed read path, but weekly/monthly public ranking still depends on runtime aggregation over materialized match stats. HLL Vietnam needs a documented snapshot model for weekly and monthly ranking windows that preserves the current RCON-first policy, keeps annual behavior intact and defines a controlled fallback strategy during transition.
|
||||
|
||||
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. Use the performance audit and the annual snapshot runbook as reference points.
|
||||
3. Define the proposed tables, keys, metadata and item payload needed for weekly/monthly ranking snapshots.
|
||||
4. Define refresh cadence and lifecycle rules for current and closed windows.
|
||||
5. Define the public fallback policy without reintroducing heavy recomputation on every request.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `docs/ranking-stats-performance-audit.md`
|
||||
- `docs/global-ranking-page-plan.md`
|
||||
- `docs/annual-ranking-snapshot-runbook.md`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/rcon_annual_rankings.py`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `docs/ranking-snapshot-read-model-plan.md`
|
||||
- `ai/tasks/done/TASK-190-design-weekly-monthly-ranking-snapshots.md`
|
||||
|
||||
## Constraints
|
||||
|
||||
- Documentación-only.
|
||||
- No modificar backend.
|
||||
- No modificar frontend.
|
||||
- No crear migraciones.
|
||||
- No implementar snapshots todavía.
|
||||
- No reactivar Elo/MMR.
|
||||
- No reintroducir Comunidad Hispana #03.
|
||||
- Mantener el diseño alineado con Python backend y con el snapshot anual existente.
|
||||
|
||||
## Validation
|
||||
|
||||
Before completing the task ensure:
|
||||
|
||||
- the plan defines `ranking_snapshots`
|
||||
- the plan defines `ranking_snapshot_items`
|
||||
- the model covers:
|
||||
- `timeframe` weekly/monthly/annual
|
||||
- `server_id`
|
||||
- `metric`
|
||||
- `window_start` and `window_end`
|
||||
- `generated_at`
|
||||
- `source`
|
||||
- `snapshot_status`
|
||||
- `item_count`
|
||||
- `limit_size`
|
||||
- `ranking_position`
|
||||
- `player_id`
|
||||
- `player_name`
|
||||
- `metric_value`
|
||||
- `matches_considered`
|
||||
- `kills`
|
||||
- `deaths`
|
||||
- `teamkills`
|
||||
- `kd_ratio`
|
||||
- `kills_per_match`
|
||||
- the refresh policy is explicit:
|
||||
- weekly current every 5-15 minutes
|
||||
- monthly current every 15-30 minutes
|
||||
- previous week/month closed and stable
|
||||
- annual manual or daily
|
||||
- the fallback policy is explicit:
|
||||
- serve snapshot if present
|
||||
- return controlled missing or use runtime fallback only by configuration if missing
|
||||
- never recalculate by default on every public request
|
||||
- the plan names impacted endpoints and expected response metadata
|
||||
- `git diff --name-only` stays within scope
|
||||
|
||||
## Outcome
|
||||
|
||||
- Snapshot design documented in `docs/ranking-snapshot-read-model-plan.md`.
|
||||
- Proposed read model uses:
|
||||
- `ranking_snapshots`
|
||||
- `ranking_snapshot_items`
|
||||
- The plan explicitly covers:
|
||||
- `timeframe` weekly/monthly/annual
|
||||
- `server_id`
|
||||
- `metric`
|
||||
- `window_start`
|
||||
- `window_end`
|
||||
- `generated_at`
|
||||
- `source`
|
||||
- `snapshot_status`
|
||||
- `item_count`
|
||||
- `limit_size`
|
||||
- per-item ranking and player fields
|
||||
- Refresh policy defined:
|
||||
- weekly current every `5` to `15` minutes
|
||||
- monthly current every `15` to `30` minutes
|
||||
- previous week/month stable once closed
|
||||
- annual manual or daily
|
||||
- Fallback policy defined:
|
||||
- serve snapshot when `ready`
|
||||
- return controlled `missing` or use runtime fallback only by configuration when snapshot is absent
|
||||
- never recalculate by default on every public request
|
||||
- Transition notes prepared for `TASK-191`:
|
||||
- weekly/monthly snapshot-first read path
|
||||
- annual remains on the existing annual snapshot implementation until a dedicated migration task consolidates storage
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: TASK-191-serve-ranking-from-snapshots-with-runtime-fallback
|
||||
title: Serve ranking from snapshots with runtime fallback
|
||||
status: done
|
||||
type: backend
|
||||
team: Backend Senior
|
||||
supporting_teams:
|
||||
- Arquitecto de Base de Datos
|
||||
- Arquitecto Python
|
||||
roadmap_item: foundation
|
||||
priority: high
|
||||
---
|
||||
|
||||
# TASK-191 - Serve ranking from snapshots with runtime fallback
|
||||
|
||||
## Goal
|
||||
|
||||
Implement the `/api/ranking` read path so weekly and monthly ranking are served from snapshots when available, with a controlled runtime fallback only when configured and only when the snapshot is missing.
|
||||
|
||||
## Context
|
||||
|
||||
Public Ranking requests currently pay the cost of runtime aggregation for weekly and monthly windows, while annual ranking already reads from snapshots. After `TASK-190` defines the snapshot read model, HLL Vietnam needs the public ranking endpoint to prefer snapshot-backed reads, preserve annual snapshot behavior and expose enough metadata for frontend and operations to understand source, freshness and fallback usage.
|
||||
|
||||
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. Implement weekly/monthly snapshot lookup according to `docs/ranking-snapshot-read-model-plan.md`.
|
||||
3. Keep annual requests on the existing annual snapshot path.
|
||||
4. Add controlled runtime fallback only for missing weekly/monthly snapshots when configuration allows it.
|
||||
5. Expose response metadata needed to distinguish snapshot-ready, snapshot-missing and runtime-fallback responses.
|
||||
6. Validate weekly, monthly, annual and invalid-request behavior.
|
||||
|
||||
## Files to Read First
|
||||
|
||||
- `AGENTS.md`
|
||||
- `ai/repo-context.md`
|
||||
- `ai/architecture-index.md`
|
||||
- `docs/ranking-snapshot-read-model-plan.md`
|
||||
- `backend/app/routes.py`
|
||||
- `backend/app/payloads.py`
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/rcon_annual_rankings.py`
|
||||
- `scripts/run-stats-validation.ps1`
|
||||
|
||||
## Expected Files to Modify
|
||||
|
||||
- `backend/app/rcon_historical_leaderboards.py`
|
||||
- `backend/app/routes.py`
|
||||
- `backend/app/payloads.py`
|
||||
- `scripts/run-stats-validation.ps1`
|
||||
- `docs/ranking-snapshot-read-model-plan.md`, solo si se documenta comportamiento final
|
||||
- `ai/tasks/done/TASK-191-serve-ranking-from-snapshots-with-runtime-fallback.md`
|
||||
|
||||
## Constraints
|
||||
|
||||
- No cambiar frontend salvo que sea imprescindible y esté documentado.
|
||||
- No crear nuevas features visuales.
|
||||
- No reactivar Elo/MMR.
|
||||
- No reintroducir Comunidad Hispana #03.
|
||||
- No usar scoreboard público como fuente primaria.
|
||||
- No recalcular rankings pesados si snapshot `ready` existe.
|
||||
- Mantener fallback runtime controlado para transición.
|
||||
- La task depende del diseño de `TASK-190`.
|
||||
|
||||
## Validation
|
||||
|
||||
Before completing the task ensure:
|
||||
|
||||
- `/api/ranking` weekly/monthly:
|
||||
- attempts snapshot read first
|
||||
- returns snapshot when status is `ready`
|
||||
- uses runtime materialized fallback only when snapshot is missing and fallback is allowed
|
||||
- returns controlled missing when snapshot is missing and fallback is not allowed
|
||||
- `/api/ranking` annual keeps current snapshot behavior
|
||||
- response metadata includes:
|
||||
- `source`
|
||||
- `snapshot_status`
|
||||
- `generated_at`
|
||||
- `freshness`
|
||||
- `fallback_used`
|
||||
- `window_start`
|
||||
- `window_end`
|
||||
- run:
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1`
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||
- probe:
|
||||
- `/api/ranking` weekly
|
||||
- `/api/ranking` monthly
|
||||
- `/api/ranking` annual
|
||||
- snapshot-missing behavior
|
||||
- invalid `metric`
|
||||
- invalid `timeframe`
|
||||
- invalid `limit`
|
||||
- confirm annual still works
|
||||
- document if snapshots are not yet generated automatically
|
||||
- `git diff --name-only` stays within scope
|
||||
|
||||
## Outcome
|
||||
|
||||
- Implemented weekly/monthly `/api/ranking` as snapshot-first:
|
||||
- snapshot `ready` rows are served from `ranking_snapshots` + `ranking_snapshot_items`
|
||||
- annual requests remain on the existing annual snapshot path
|
||||
- Added controlled runtime fallback behavior:
|
||||
- fallback is used only when the weekly/monthly snapshot is missing
|
||||
- fallback is controlled by `HLL_BACKEND_RANKING_RUNTIME_FALLBACK_ENABLED`
|
||||
- default remains enabled for transition
|
||||
- setting the variable to `false` returns controlled `snapshot_status='missing'` with empty items
|
||||
- Response metadata now distinguishes:
|
||||
- snapshot-ready
|
||||
- snapshot-missing
|
||||
- runtime-fallback
|
||||
- Metadata exposed on ranking responses now includes:
|
||||
- `source`
|
||||
- `snapshot_status`
|
||||
- `generated_at`
|
||||
- `freshness`
|
||||
- `fallback_used`
|
||||
- `window_start`
|
||||
- `window_end`
|
||||
- Validation completed:
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-stats-validation.ps1`
|
||||
- `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1`
|
||||
- Validation script now proves:
|
||||
- normal route-contract behavior
|
||||
- snapshot-ready behavior using temporary fixture rows
|
||||
- snapshot-missing behavior with runtime fallback disabled
|
||||
- Remaining operational dependency:
|
||||
- snapshot tables now exist on first access, but snapshot rows are not generated automatically yet; a future generator/job still needs to populate weekly/monthly snapshots for production use
|
||||
|
||||
## 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