Skip to content

perf: match page stats views compute only the match being viewed - #639

Merged
lukepolo merged 3 commits into
mainfrom
perf/match-stats-views-pushdown
Oct 16, 2026
Merged

lukepolo merged 3 commits into
mainfrom
perf/match-stats-views-pushdown

Conversation

@lukepolo

@lukepolo lukepolo commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

The match page's buy-type, map-stats, opening-duel and roles views computed their result for every match on the panel and only then kept the one being viewed. On 5stack.gg that made them 105–266ms per read, and the cost grows with the whole database.

  • A filter can only pass below a window function, DISTINCT ON or GROUP BY on one of its keys. It also can't reach into a CTE used twice unless that CTE is NOT MATERIALIZED.

  • match_id is now a key of each of those across the five views, and the kill and first-kill joins match on match_id. Opening duels' twice-used CTEs (round_sides, fk) are NOT MATERIALIZED. The roles view instead builds openings from one pass over first_kills, because forcing it inline doubled the reads no match filter narrows (the profile, role-filtered leaderboards).

  • match_map_id already implies match_id, so the output is unchanged; player_kills.match_id is NOT NULL with a foreign key to matches.

  • Same rows: each view's full output on a 600-match bench hashes identically before and after.

  • Measured through Hasura's own SQL, with 100,800 kills on the panel:

    View Before After
    v_match_player_buy_types 258ms, 4,109 buffers 3.9ms, 199
    v_match_lineup_buy_types 80ms, 538 0.7ms, 66
    v_match_lineup_map_stats 104ms, 52,449 1.5ms, 207
    v_match_player_opening_duels 439ms, 283,769 2.2ms, 391
    v_player_match_map_roles 156ms, 114,231 1.6ms, 176
  • test/match-stats-views.spec.ts pins, on hand-built matches:

    • per-player kills/deaths per matchup
    • lineup wins per matchup
    • opening and man-advantage stats
    • opening duels including a traded death
    • role picks (Entry, Support, Rifler)

    These pass on main too, and views.spec.ts runs unchanged. A plan test pins one scan of player_kills per use for roles read by player.

  • A guard reads one match at 75 and at 150 other matches, with fresh statistics both times. On main the five views grow +324 / +78 / +2,019 / +9,273 / +5,617 buffers; on this PR they grow at most +16.

v_match_player_buy_types, v_match_lineup_buy_types and v_match_lineup_map_stats partitioned and grouped by match_map_id only, so the page's match_id filter could not pass below the window, DISTINCT ON or GROUP BY: every read computed the views for every match on the panel. match_id is now a key of each, which match_map_id already implies, so the rows are unchanged.
…ed, and the guard compares like with like

Review found v_match_player_opening_duels and v_player_match_map_roles had the same blockers (plus CTEs used twice, which Postgres materializes past any filter) and v_match_lineup_map_stats still scanned match_maps in fk_lineups. match_id is now a key of each, those CTEs are NOT MATERIALIZED, and fk_lineups joins on match_id. The growth guard measured its baseline on stale statistics and so compared two plans; it now ANALYZEs and compares 75 against 150 other matches, covering all five views. New hand-derived tests for opening duels (with a trade) and roles.
Review found NOT MATERIALIZED on first_kills made reads that no match filter narrows (the profile by steam_id, role-filtered leaderboards) scan every kill twice: 131,958 -> 209,429 buffers. opening now reads first_kills once through a LATERAL VALUES pair, so the CTE needs no forcing; those reads are back to base and match reads stay pushed down. A plan test pins one scan of player_kills per use.
@lukepolo
lukepolo force-pushed the perf/match-stats-views-pushdown branch from c6f50a1 to 4cabd7a Compare October 16, 2026 16:04
@lukepolo
lukepolo merged commit 950d580 into main Oct 16, 2026
7 checks passed
@lukepolo
lukepolo deleted the perf/match-stats-views-pushdown branch October 16, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant