"""Indeks pod cale ORDER BY listy scen sortowanej po tytule. `sort=title` konczy sie na `title_normalized ASC, created_at DESC, id DESC` (PK jako ostateczny tie-break, zeby strony sie nie nakladaly). Stary `ix_scenes_title_normalized` pokrywal tylko pierwszy czlon, wiec Postgres dokladal Incremental Sort, a ten przed oddaniem PIERWSZEGO wiersza musi zebrac caly kubelek scen o tym samym tytule. Na stronie 1 glownego feedu ciagnal przez to 1084 wiersze zamiast 51 (709 ms zimno), a z filtrem taga „blowjob" nie zmiescil sie w 90 s. Pomiar na produkcji 2026-09-16, ten sam zestaw filtrow, po rozgrzewce: strona 1 709 ms -> 12 ms (skan czyta 81 wpisow indeksu zamiast 3064) strona 5 27 ms -> 9 ms + tag >90 s -> 1,1 s Kierunki w indeksie musza byc dokladnie takie jak w ORDER BY, bo sort jest mieszany (ASC, DESC, DESC) i inaczej skan nie przejdzie po kolei. `ix_scenes_title_normalized` zostaje: sluzy tez zwyklym lookupom po tytule (2,2 mln skanow), a odpiecie go od tego indeksu to osobna decyzja, nie skutek uboczny tej migracji. CONCURRENTLY w autocommit_block, jak w 0035 i 0038. Nieudany CONCURRENTLY zostawia indeks INVALID; wtedy DROP INDEX i ponownie. Revision ID: 0039_scenes_title_created_id_idx Revises: 0038_scenes_studio_effdate_idx Create Date: 2026-09-16 """ from __future__ import annotations from alembic import op revision = "0039_scenes_title_created_id_idx" down_revision: str | None = "0038_scenes_studio_effdate_idx" branch_labels = None depends_on = None def upgrade() -> None: with op.get_context().autocommit_block(): op.execute( "CREATE INDEX CONCURRENTLY IF NOT EXISTS ix_scenes_title_created_id_desc " "ON scenes (title_normalized, created_at DESC, id DESC)" ) def downgrade() -> None: with op.get_context().autocommit_block(): op.execute("DROP INDEX CONCURRENTLY IF EXISTS ix_scenes_title_created_id_desc")