Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions ddl/migrations/0198_aggregate_user_score_negative_idx.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- Partial index covering only shadowbanned users (aggregate_user.score < 0).
--
-- Several handlers — v1_event_comments, v1_events_remix_contests,
-- v1_fan_club_feed, v1_track_comments, v1_track_comment_count — materialize
-- a `low_abuse_score` CTE via:
--
-- SELECT user_id FROM aggregate_user WHERE score < 0
--
-- Without a covering index this is a sequential scan over the entire
-- aggregate_user table (one row per user, in the millions). On cold cache,
-- /v1/events/remix-contests?status=all measured ~22s end-to-end, dominated
-- by that seq scan; on warm cache the same call returns in ~100ms.
--
-- aggregate_user.score < 0 is a very small fraction of users (shadowbanned
-- accounts only), so a partial index is dramatically cheaper than a full
-- btree on `score`.
--
-- Size budget: thousands of matching rows × ~12 bytes ≈ tens of KB.
--
-- NOTE: intentionally NOT wrapped in BEGIN/COMMIT so CREATE INDEX
-- CONCURRENTLY can run without holding ACCESS EXCLUSIVE on aggregate_user.
-- IF NOT EXISTS keeps the migration idempotent.

create index concurrently if not exists idx_aggregate_user_score_negative
on aggregate_user (user_id)
where score < 0;

comment on index idx_aggregate_user_score_negative is
'Partial index for the shadowban-author CTEs (score < 0); avoids a full table scan of aggregate_user.';
Loading