perf(api): kilka tagow od najnowszych idzie po indeksie najrzadszego z nich

Z dwoma tagami (rough-sex + anal) lista szla stara droga po calym katalogu, 7,4 s na zimnym
cache. Teraz dolaczamy najrzadszy tag po ix_scene_tags_tag_sort, a reszta zostaje jako
EXISTS: 0,2 s. Dolaczona tabela ma alias, inaczej EXISTS po scene_tags korelowal sie z nia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-09-18 15:11:45 +02:00
parent 7f2fdfaed0
commit 73853eb9ed

View file

@ -466,22 +466,25 @@ def list_scenes(
# ix_scenes_created_at_desc zamiast materializować wszystkie scene_tags. Slug-JOIN
# ukrywał tag_id przed plannerem → używał średniej (8.4M/11541≈726) → zły plan
# (4-12s). Z literałem: ~20ms. Zob. też _build... light mode.
# Jeden tag + sort od najnowszych (ekran tagu w apce): zamiast EXISTS dolaczamy wiersz
# scene_tags i sortujemy po jego `sort_date`, patrz galaz sort == "release_date".
# Sort od najnowszych z filtrem tagu: jeden tag zamiast EXISTS dolaczamy jako wiersz
# scene_tags i sortujemy po jego `sort_date` (galaz sort == "release_date"). Przy kilku
# tagach dolaczamy NAJRZADSZY, bo spacer po indeksie konczy sie najpozniej na jego
# ostatniej scenie; reszta zostaje jako EXISTS. rough-sex + anal: 7,4 s na zimnym cache
# po starej sciezce (2026-09-18).
tag_sort_id: uuid.UUID | None = None
if tag_slug_list:
id_by_slug = dict(
session.execute(
select(Tag.slug, Tag.id).where(Tag.slug.in_(tag_slug_list))
).all()
)
tag_rows = session.execute(
select(Tag.slug, Tag.id, Tag.scene_count).where(Tag.slug.in_(tag_slug_list))
).all()
id_by_slug = {slug: tid for slug, tid, _ in tag_rows}
if sort == "release_date" and tag_rows:
tag_sort_id = min(tag_rows, key=lambda r: r[2] or 0)[1]
for slug in tag_slug_list:
tag_id = id_by_slug.get(slug)
if tag_id is None:
base = base.where(false()) # nieznany slug → brak wyników
break
if len(tag_slug_list) == 1 and sort == "release_date":
tag_sort_id = tag_id
if tag_id == tag_sort_id:
continue
base = base.where(
exists(
@ -651,16 +654,18 @@ def list_scenes(
# od strefy sesji, stad jawne AT TIME ZONE 'UTC'. Indeks:
# ix_scenes_effective_date_desc (migracja 0033).
if tag_sort_id is not None:
# Z filtrem jednego tagu ta sama data siedzi w scene_tags.sort_date (wyzwalacz,
# Z filtrem tagu ta sama data siedzi w scene_tags.sort_date (wyzwalacz,
# migracja 0042) i idzie po ix_scene_tags_tag_sort (0043): od najnowszej sceny
# tagu, koniec po LIMIT. Po samym `scenes` Postgres sprawdzal tag scena po scenie
# od najnowszej w calym katalogu i przy tagach rzedu 40-60 tys. scen (`rough-sex`)
# przegladal 46 tys. scen na jedna strone: 7-15 s na zimnym cache (GOON-64).
# W obrebie jednego dnia kolejnosc po id, nie po created_at: indeks jest
# wezszy, a stronicowanie i tak stabilne.
# Alias, bo przy kilku tagach EXISTS po scene_tags skorelowalby sie z tym JOIN-em.
st = aliased(SceneTag)
ordered = base.join(
SceneTag, and_(SceneTag.scene_id == Scene.id, SceneTag.tag_id == tag_sort_id)
).order_by(SceneTag.sort_date.desc(), SceneTag.scene_id.desc())
st, and_(st.scene_id == Scene.id, st.tag_id == tag_sort_id)
).order_by(st.sort_date.desc(), st.scene_id.desc())
else:
ordered = base.order_by(
_effective_date_desc(), Scene.created_at.desc(), Scene.id.desc()