fix(api): bez planow ogolnych i szybkie listy serwisow, ktore stoja
Some checks failed
Backend tests / test (push) Has been cancelled
Some checks failed
Backend tests / test (push) Has been cancelled
- db: prepare_threshold=None. psycopg przygotowywal zapytanie po 5 wykonaniach na polaczeniu, a Postgres przechodzil potem na plan ogolny. Lista duzego tagu na takim planie: "pov" 44 s zamiast 0,46 s, "masturbation" 46 s zamiast 0,18 s. Stad listy ladowaly sie raz szybko, raz minute, zaleznie od polaczenia z puli (tez GOON-73/75). - Lista jednego serwisu bez frazy: przejscie od najnowszych z limitem 1 s, a po nim start od scen serwisu (do 150 tys.). Serwis, ktory stoi, mial sceny gleboko: superporn 72-92 s -> 1,7 s, yespornvip 15 s -> 1,5 s, hentaiplay 9 s -> 0,06 s. Porownanie ze starym kodem na 88 listach serwisow: wyniki identyczne przy zapytaniach puszczonych po sobie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3349700f72
commit
2a2c0d7e9d
2 changed files with 42 additions and 8 deletions
|
|
@ -251,6 +251,16 @@ _ORIGIN_DRIVEN_MAX_SCENES = 60_000
|
||||||
# a przy sort=studio przejscie po studiach szuka trafien w kazdym z 33 tys. studiow
|
# a przy sort=studio przejscie po studiach szuka trafien w kazdym z 33 tys. studiow
|
||||||
# ("sunny leone" 15 min), podczas gdy trigramy + sort trafien to 0,37 s.
|
# ("sunny leone" 15 min), podczas gdy trigramy + sort trafien to 0,37 s.
|
||||||
_RARE_PHRASE_HITS = 4
|
_RARE_PHRASE_HITS = 4
|
||||||
|
# Lista jednego serwisu bez frazy (ekran serwisu w apce). Planer idzie od najnowszych scen
|
||||||
|
# calego katalogu i sprawdza, czy scena jest z tego serwisu. Dla serwisu, ktory stoi,
|
||||||
|
# jego sceny sa gleboko: superporn (37 tys., ostatnia 20.08) 92 s na strone 1, bo
|
||||||
|
# przejscie mijalo 1,1 mln nowszych scen; yespornvip 23 s (pomiar 2026-09-23). Dlatego
|
||||||
|
# przejscie dostaje limit czasu, a po nim lista zaczyna od scen serwisu. Limit 1 s, bo
|
||||||
|
# u zywych srednich serwisow przejscie trwa ok. 1 s (hdporngg), a start od scen serwisu
|
||||||
|
# kosztuje wtedy wiecej (5 s na zimno). Powyzej 150 tys. scen (porndoe 279 tys.) start
|
||||||
|
# od serwisu jest wolniejszy niz przejscie, wiec zostaje zwykly plan.
|
||||||
|
_ORIGIN_LIST_WALK_BUDGET_MS = 1000
|
||||||
|
_ORIGIN_LIST_MAX_SCENES = 150_000
|
||||||
_TRGM_RUN_RE = re.compile(r"[^\W_]{3}")
|
_TRGM_RUN_RE = re.compile(r"[^\W_]{3}")
|
||||||
_TITLE_SAMPLE_TTL = 3600.0
|
_TITLE_SAMPLE_TTL = 3600.0
|
||||||
_TITLE_SAMPLE: dict = {"ts": 0.0, "vals": ()}
|
_TITLE_SAMPLE: dict = {"ts": 0.0, "vals": ()}
|
||||||
|
|
@ -334,10 +344,7 @@ def _search_walk_rows(
|
||||||
if ids is None:
|
if ids is None:
|
||||||
# Bez trigramu zwykly plan czyta caly indeks (44 s), wiec lepiej pusto.
|
# Bez trigramu zwykly plan czyta caly indeks (44 s), wiec lepiej pusto.
|
||||||
return None if budget == _SEARCH_WALK_BUDGET_MS else []
|
return None if budget == _SEARCH_WALK_BUDGET_MS else []
|
||||||
if not ids:
|
return _scenes_in_order(session, ids)
|
||||||
return []
|
|
||||||
by_id = {s.id: s for s in session.execute(select(Scene).where(Scene.id.in_(ids))).scalars()}
|
|
||||||
return [by_id[i] for i in ids if i in by_id]
|
|
||||||
|
|
||||||
|
|
||||||
def _rare_title_phrase(q: str, sample) -> bool:
|
def _rare_title_phrase(q: str, sample) -> bool:
|
||||||
|
|
@ -356,7 +363,9 @@ def _origin_driven_worth(q: str, sample) -> bool:
|
||||||
return not _rare_title_phrase(q, sample)
|
return not _rare_title_phrase(q, sample)
|
||||||
|
|
||||||
|
|
||||||
def _origin_scene_ids(session: Session, origin: str) -> list[uuid.UUID] | None:
|
def _origin_scene_ids(
|
||||||
|
session: Session, origin: str, max_scenes: int = _ORIGIN_DRIVEN_MAX_SCENES
|
||||||
|
) -> list[uuid.UUID] | None:
|
||||||
"""Id scen z zywym zrodlem serwisu albo None, gdy serwis jest za duzy na te sciezke.
|
"""Id scen z zywym zrodlem serwisu albo None, gdy serwis jest za duzy na te sciezke.
|
||||||
|
|
||||||
Wielkosc bierzemy z source_stats (liczone co 6 h), zeby dla xvideos nie ciagnac
|
Wielkosc bierzemy z source_stats (liczone co 6 h), zeby dla xvideos nie ciagnac
|
||||||
|
|
@ -365,17 +374,25 @@ def _origin_scene_ids(session: Session, origin: str) -> list[uuid.UUID] | None:
|
||||||
from app.models.source_stats import SourceStats
|
from app.models.source_stats import SourceStats
|
||||||
|
|
||||||
size = session.execute(select(SourceStats.scenes).where(SourceStats.origin == origin)).scalar()
|
size = session.execute(select(SourceStats.scenes).where(SourceStats.origin == origin)).scalar()
|
||||||
if size is not None and size > _ORIGIN_DRIVEN_MAX_SCENES:
|
if size is not None and size > max_scenes:
|
||||||
return None
|
return None
|
||||||
ids = list(
|
ids = list(
|
||||||
session.execute(
|
session.execute(
|
||||||
select(PlaybackSource.scene_id)
|
select(PlaybackSource.scene_id)
|
||||||
.where(PlaybackSource.origin == origin, PlaybackSource.dead_at.is_(None))
|
.where(PlaybackSource.origin == origin, PlaybackSource.dead_at.is_(None))
|
||||||
.distinct()
|
.distinct()
|
||||||
.limit(_ORIGIN_DRIVEN_MAX_SCENES + 1)
|
.limit(max_scenes + 1)
|
||||||
).scalars()
|
).scalars()
|
||||||
)
|
)
|
||||||
return ids if len(ids) <= _ORIGIN_DRIVEN_MAX_SCENES else None
|
return ids if len(ids) <= max_scenes else None
|
||||||
|
|
||||||
|
|
||||||
|
def _scenes_in_order(session: Session, ids: list[uuid.UUID]) -> list[Scene]:
|
||||||
|
"""Pelne wiersze scen dla listy id, w kolejnosci tej listy."""
|
||||||
|
if not ids:
|
||||||
|
return []
|
||||||
|
by_id = {s.id: s for s in session.execute(select(Scene).where(Scene.id.in_(ids))).scalars()}
|
||||||
|
return [by_id[i] for i in ids if i in by_id]
|
||||||
|
|
||||||
|
|
||||||
def _split_csv(raw: str | None) -> list[str]:
|
def _split_csv(raw: str | None) -> list[str]:
|
||||||
|
|
@ -1037,6 +1054,17 @@ def list_scenes(
|
||||||
Scene.id
|
Scene.id
|
||||||
== any_(bindparam("origin_scene_ids", origin_ids, type_=ARRAY(PG_UUID(as_uuid=True))))
|
== any_(bindparam("origin_scene_ids", origin_ids, type_=ARRAY(PG_UUID(as_uuid=True))))
|
||||||
)
|
)
|
||||||
|
if rows is None and origin and not q and sort in ("created_at", "release_date"):
|
||||||
|
walked = _search_walk_ids(session, ordered, _off, _want, _ORIGIN_LIST_WALK_BUDGET_MS)
|
||||||
|
if walked is not None:
|
||||||
|
rows = _scenes_in_order(session, walked)
|
||||||
|
else:
|
||||||
|
origin_ids = _origin_scene_ids(session, origin, _ORIGIN_LIST_MAX_SCENES)
|
||||||
|
if origin_ids is not None:
|
||||||
|
ordered = ordered.where(
|
||||||
|
Scene.id
|
||||||
|
== any_(bindparam("origin_scene_ids", origin_ids, type_=ARRAY(PG_UUID(as_uuid=True))))
|
||||||
|
)
|
||||||
if rows is None and origin_ids is None and q and (
|
if rows is None and origin_ids is None and q and (
|
||||||
sort == "created_at" or (sort == "release_date" and tag_sort_id is None)
|
sort == "created_at" or (sort == "release_date" and tag_sort_id is None)
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,16 @@ from app.config import get_settings
|
||||||
|
|
||||||
_settings = get_settings()
|
_settings = get_settings()
|
||||||
|
|
||||||
|
# prepare_threshold=None: psycopg domyslnie przygotowuje zapytanie po 5 wykonaniach na
|
||||||
|
# polaczeniu, a Postgres po kolejnych 5 potrafi przejsc na plan ogolny, niezalezny od
|
||||||
|
# parametrow. Dla list tagu to katastrofa: plan dobry dla malego tagu na duzym ("pov")
|
||||||
|
# trwal 44 s zamiast 0,46 s (pomiar 2026-09-23), wiec lista ladowala sie raz szybko, raz
|
||||||
|
# minute, zaleznie od tego, ktore polaczenie z puli trafilo. Planowanie od nowa to kilka ms.
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
_settings.database_url,
|
_settings.database_url,
|
||||||
pool_pre_ping=True,
|
pool_pre_ping=True,
|
||||||
future=True,
|
future=True,
|
||||||
|
connect_args={"prepare_threshold": None},
|
||||||
)
|
)
|
||||||
|
|
||||||
SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False, future=True)
|
SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False, future=True)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue