feat(ingestor): per-minute timeline snapshots (powerspike foundation)#568
Conversation
Capture a snapshot at every minute mark (1..30) instead of the 5 fixed marks (5/10/15/20/30), so reads can align state to arbitrary event times (item completed, level 6/11/16) and draw a power curve over time — the foundation for the reworked powerspike read. No schema change: IntervalMinute is already an int and the unique index (MatchId, ParticipantId, IntervalMinute) still holds. Denser sampling is forward-only (no backfill on the dev API key); games ingested earlier keep their 5 marks. Pin ChampionTimelineLeadsQueryService to the canonical marks (5/10/15/20/30) so the leads curve renders identically across old (5-mark) and new (per-minute) games and every interval keeps the full game population behind the sample floor. Refs #567
There was a problem hiding this comment.
Review
Ce que fait ce PR
Passe l'echantillonnage des snapshots de 5 marques fixes (5/10/15/20/30 min) a 1 snapshot par minute (1..30), sans changement de schema. Le service de leads existant est epingle aux 5 marques canoniques pour maintenir une grille coherente entre les jeux anciens et nouveaux.
Points verifies
- Enumerable.Range(1, MaxIntervalMinute) genere bien 1..30 (aucun off-by-one).
- Filtre s2 : le second SelectMany joint sur s2.IntervalMinute == self.s1.IntervalMinute, ce qui contraint implicitement s2 aux memes intervalles que s1 — pas de bug de produit cartesien.
- EF Core traduit correctement int[].Contains() en IN (5, 10, 15, 20, 30).
- SelectFrame V-shape : l'optimisation early-break reste valide a 30 iterations/minute.
- Duplication de [5, 10, 15, 20, 30] : intentionnelle et documentee — le commentaire explique explicitement pourquoi le service de leads ne derive pas de IntervalMinutes.
Aucun point bloquant identifie.
Suggestions (non bloquantes) :
Voir commentaire inline sur TimelineSnapshotBuilderTests.cs : un cas de test avec frames denses permettrait de prouver directement le nouveau comportement (minutes 1-N emises) plutot que de se reposer uniquement sur la regression des frames epars.
The existing tests only proved the regression case (sparse frames still yield the 5/10 marks). Add a dense-timeline case (a frame on every minute 1..10) that asserts a snapshot is emitted for each covered minute — direct coverage of the per-minute change. Refs #567
There was a problem hiding this comment.
Changeset propre et bien ciblé. Aucun point bloquant.
Ce qui a été vérifié :
- TimelineSnapshotBuilder : Enumerable.Range(1, 30) produit [1..30] ; MaxIntervalMinute en private const est approprié.
- ChampionTimelineLeadsQueryService : LeadIntervalMinutes.Contains() se traduit en IN (5,10,15,20,30) par EF Core. Le filtre explicite sur s2 est inutile car le join s2.IntervalMinute == self.s1.IntervalMinute (ligne 99) contraint implicitement l'opposant aux mêmes marques canoniques — logique correcte.
- Tests : Build_EmitsRowForEveryMinuteCoveredByFrames valide le comportement clé ; la rétrocompat via frames sparse est couverte par le test existant.
- Pas de changement de schéma : l'index unique (MatchId, ParticipantId, IntervalMinute) reste cohérent.
Suggestions (non bloquantes) : voir commentaire inline — ajouter un second participant au nouveau test confirmerait l'absence d'interférence entre participants sur la sélection de frame.
First step of the reworked powerspike feature.
Why
The current snapshots are only at 5 fixed marks (5/10/15/20/30 min) — too coarse to measure an item's impact around its completion time. To draw a power curve over time and align spikes to events (items completed, levels 6/11/16), we need per-minute resolution.
Change
TimelineSnapshotBuilder: sample every minute (1..30) instead of 5 fixed marks. Riot frames are ~1/min, so each mark resolves to its own frame.IntervalMinuteis alreadyint; the unique index(MatchId, ParticipantId, IntervalMinute)still holds. No migration / no compiled-model regen.ChampionTimelineLeadsQueryService: pinned to the canonical marks (5/10/15/20/30) so the existing leads curve renders identically across old (5-mark) and new (per-minute) games, and every interval keeps the full population behind the sample floor.Forward-only
Denser sampling applies to newly-ingested games only (no backfill on the dev API key) — the powerspike read will fill in as games accumulate.
Verification
dotnet build --configuration Release: 0 warningsNext (separate PR)
The powerspike read model + spike-detection (power-curve composite: combat + gold lead, opponent-relative; events annotated; spike magnitude = slope change after each event).
Closes #567