Obie listy chodzily na produkcji w dziesiatkach sekund, bo klucz sortu nie mial nic wspolnego z filtrami widocznosci (zywy playback, odsiew wertykalow, odsiew stubow). sort=studio: nazwa studia siedzi w dolaczonej tabeli, wiec zaden indeks scen jej nie zna i planer materializowal caly przefiltrowany katalog (ponad 2 mln wierszy), po czym sortowal go w calosci. Odwracamy zapytanie: idziemy po studiach w kolejnosci nazwy i dla kazdego dobieramy jego sceny przez LATERAL po ix_scenes_studio_effective_date_desc. Zewnetrzny skan jest posortowany, wiec LIMIT urywa go po kilkunastu studiach. Sceny bez studia dobiera osobny ogon, tak jak wczesniej robilo NULLS LAST. Przy filtrze po studiach zawezamy tez sam skan studiow, inaczej szedlby przez wszystkie 33 tys. sort=title: nowy indeks na cale ORDER BY (title_normalized, created_at DESC, id DESC). Sam title_normalized zostawial Incremental Sort, a ten przed oddaniem pierwszego wiersza musi zebrac caly kubelek scen o tym samym tytule. Pomiary na produkcji (2026-09-16, ten sam zestaw filtrow, po rozgrzewce): studio strona 1 483 s -> 41 ms studio strona 5 ponad 5 min -> 38 ms studio + tag blowjob 71 s -> 226 ms studio + min_duration 48 s -> 42 ms studio + studio_slugs 255 ms -> 44 ms tytul strona 1 709 ms -> 12 ms (81 wpisow indeksu zamiast 3064) tytul + tag blowjob ponad 90 s -> 1,5 s Wynik jest identyczny co do wiersza: pierwsze 251 pozycji pelnego katalogu porownane pozycja po pozycji ze starym zapytaniem daje zero roznic, tak samo dwa okna alfabetyczne studiow (w tym Brazzers, zeby sprawdzic wewnetrzny LIMIT). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
48 lines
1.9 KiB
Python
48 lines
1.9 KiB
Python
"""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")
|