Files
recflare/apps/rooms/migrations/0031_room_engagement_counters.sql
2026-10-20 21:03:54 -04:00

28 lines
1.6 KiB
SQL

-- Cheer and favorite counters on `room`, as columns — the cheer/favorite twins of
-- `visits` (0011).
--
-- Generated from packages/domain/src/rooms-db.ts (ROOM_SCHEMA_DDL) — keep in sync.
--
-- Until now `Stats.CheerCount`/`FavoriteCount` were DERIVED on every read by aggregating
-- the per-player `interaction` rows (`SELECT room_id, SUM(cheered), SUM(favorited) …
-- GROUP BY room_id`). 0021's covering index made the per-room form cheap, but the browse
-- and discovery feeds rank EVERY listable room, so they ran the unfiltered form — a walk
-- of the whole index per call — and the discovery page runs the hot feed once per
-- category row. That was the bulk of the database's reads.
--
-- Now each counter is kept current by the toggles themselves: the same batch that flips a
-- player's `interaction.cheered`/`favorited` applies a `cheers = cheers ± 1` to the room,
-- so a cheer still shows immediately and concurrent toggles can't lose each other. The
-- per-player rows stay the record of WHO cheered; the columns are the count of them.
--
-- The backfill below is the one remaining full aggregate, run once here. `room_id` is a
-- virtual generated column, so the correlated subquery resolves it per row; the sums come
-- off `idx_interaction_room` without touching the table.
ALTER TABLE room ADD COLUMN cheers INTEGER NOT NULL DEFAULT 0;
ALTER TABLE room ADD COLUMN favorites INTEGER NOT NULL DEFAULT 0;
UPDATE room SET
cheers = (SELECT COALESCE(SUM(cheered), 0) FROM interaction WHERE interaction.room_id = room.room_id),
favorites = (SELECT COALESCE(SUM(favorited), 0) FROM interaction WHERE interaction.room_id = room.room_id);