Prosba uzytkownika (zgloszenie 17376139). Backend mial juz parametr `min_quality_p`, ale w praktyce byl NIEUZYWALNY i to zajelo wiekszosc pracy. Wydajnosc, mierzona na produkcji przez EXPLAIN ANALYZE, prog 1080p: EXISTS na playback_sources (stan zastany) 32 s 3 556 612 odwiedzonych scen + indeks funkcyjny per-scena 24 s JOIN z DISTINCT po jakosci 1,1 s W IZOLACJI ten sam JOIN + reszta filtrow feedu 52 s planer wraca do skanu po dacie kolumna scenes.max_quality_p (0029) 2,0-2,6 s + indeksy czesciowe pod progi (0030) 0,09-0,11 s Wniosek, ktory kosztowal najwiecej: ksztaltem zapytania sie tego NIE dalo zalatwic. Koszt to LICZBA odwiedzonych scen, nie pojedynczy test, a przy `ORDER BY created_at DESC LIMIT 24` planer i tak wracal do przechodzenia scen po dacie, gdy tylko doszly pozostale filtry (bramka wertykalow, heurystyka szkieletow, prog dlugosci). Stad denormalizacja + indeksy czesciowe z predykatem progu, ktore daja gotowe sortowanie. Semantyka SUROWA, swiadomie: `quality` zapisujemy oportunistycznie, zna ja 6,5% scen (210 302 z 3 236 368), najlepsze zrodlo ma 26% pokrycia. Wariant lagodny, przepuszczajacy sceny o nieznanej jakosci, odrzucony po zmierzeniu: odsiewalby 88 647 z 3,2 mln, czyli nie realizowalby intencji "nie chce ogladac ziemniaka". Filtr pokazuje wiec tylko jakosc POTWIERDZONA, a UI mowi to wprost podpisem pod przelacznikiem, bo inaczej gwaltowny spadek liczby wynikow wyglada na zepsuta aplikacje. Warianty tekstowe 4K/UHD usuniete z warunku: 0 wystapien na 238 452 zywe zrodla, a `OR ilike` blokowal uzycie indeksu. Odswiezanie w taxonomy_counts (co 3h), z czyszczeniem do NULL gdy scena traci zrodlo z jakoscia. Backfill: 210 302 sceny w 2m56s. Zweryfikowane na emulatorze: sekcja MIN QUALITY renderuje sie z podpisem ostrzegawczym. Lista wyszla pusta, ale to build debugowy dostajacy 403 (podpis debug spoza allowlisty), nie blad filtra: ten sam URL z poprawnym podpisem oddaje 50 wynikow w 0,12 s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 lines
2.1 KiB
Python
50 lines
2.1 KiB
Python
"""scenes.max_quality_p: zdenormalizowana najwyzsza znana jakosc zrodel sceny
|
|
|
|
Po co, skoro filtr dalo sie napisac zapytaniem: NIE dalo sie go napisac szybko.
|
|
Zmierzone na produkcji 2026-08-18 (EXPLAIN ANALYZE, prog 1080p):
|
|
|
|
EXISTS na playback_sources 32 s (3 556 612 odwiedzonych scen)
|
|
+ indeks funkcyjny per-scena 24 s
|
|
JOIN z DISTINCT po jakosci 1,1 s W IZOLACJI, ale
|
|
ten sam JOIN + reszta filtrow 52 s (planer wraca do przechodzenia scen)
|
|
|
|
Ostatni wiersz jest sednem: w polaczeniu z bramka wertykalow, heurystyka szkieletow
|
|
i progiem dlugosci planer i tak wybiera przejscie `scenes` po `created_at` i testowanie
|
|
warunku scena po scenie. Kaztaltem zapytania sie tego nie ominie, bo koszt to LICZBA
|
|
odwiedzonych scen, a nie pojedynczy test. Skalarna kolumna zdejmuje problem: staje sie
|
|
zwyklym filtrem, takim jak `duration_sec`, ktory jest szybki.
|
|
|
|
NULL = nie znamy jakosci zadnego zywego zrodla (93,5% katalogu: `quality` zapisujemy
|
|
oportunistycznie, tylko gdy listing zrodla ja podaje). Filtr celowo takie sceny ODSIEWA,
|
|
patrz komentarz przy QUALITY_OPTIONS w mobile/src/screens/ScenesFilterModal.tsx.
|
|
|
|
Revision ID: 0029_scenes_max_quality
|
|
Revises: 0028_phash_backfill_queue
|
|
Create Date: 2026-08-18
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0029_scenes_max_quality"
|
|
down_revision: str | None = "0028_phash_backfill_queue"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("scenes", sa.Column("max_quality_p", sa.SmallInteger(), nullable=True))
|
|
# (max_quality_p, created_at DESC): filtr to zakres po pierwszej kolumnie, a sort
|
|
# feedu idzie po drugiej. Partial, bo 93,5% scen ma NULL i nie ma po co ich indeksowac.
|
|
op.create_index(
|
|
"ix_scenes_max_quality_created",
|
|
"scenes",
|
|
["max_quality_p", sa.text("created_at DESC")],
|
|
postgresql_where=sa.text("max_quality_p IS NOT NULL"),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_scenes_max_quality_created", table_name="scenes")
|
|
op.drop_column("scenes", "max_quality_p")
|