perf(feed): index the default published_at DESC NULLS LAST sort (E2 S1, PERF-1)
The feed's default `newest` sort is `published_at DESC NULLS LAST, id ASC`, but the only published_at index was a plain ASC btree — read backwards it satisfies DESC NULLS FIRST, not NULLS LAST — so every feed page fell back to a full `videos` seq scan + top-N sort. Migration 0067 adds the two matching indexes (global + per-channel) CONCURRENTLY and drops the now-redundant single-column ix_videos_channel_id (the channel composite leads with channel_id, a strict superset). Measured on the prod-sized localdev reseed (257,312 videos, prod planner): - global newest: 44,747 buffers / 154 ms -> 60 buffers / 0.32 ms - per-channel(12.6k): 7,604 buffers / 96 ms -> 54 buffers / 0.14 ms CONCURRENTLY build of both on 257k rows: 1.7s total (~0 downtime). This is the likely root cause of the "prod feed is slow" symptom. Model: drop the inline index=True on Video.channel_id (the composite supersedes it); the composites are migration-defined like the search_vector GIN. Gate green (334 pytest incl. the migration drift + downgrade/re-upgrade round-trip).
This commit is contained in:
@@ -251,8 +251,12 @@ class Video(Base, TimestampMixin):
|
||||
__tablename__ = "videos"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(16), primary_key=True) # video id
|
||||
# No standalone channel_id index: the composite `ix_videos_channel_pub_desc`
|
||||
# (channel_id, published_at DESC NULLS LAST, id) from migration 0067 leads with channel_id, so it
|
||||
# serves every channel_id lookup (FK cascade, sync upsert, channel-detail) AND the per-channel feed
|
||||
# sort. Like the search_vector GIN, this composite is migration-defined, not an inline `index=True`.
|
||||
channel_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("channels.id", ondelete="CASCADE"), index=True
|
||||
ForeignKey("channels.id", ondelete="CASCADE")
|
||||
)
|
||||
title: Mapped[str | None] = mapped_column(Text) # normalized for display (see app.titles)
|
||||
original_title: Mapped[str | None] = mapped_column(Text) # raw YouTube title, preserved
|
||||
|
||||
Reference in New Issue
Block a user