fix(dedup): samozlaczenie po phashu odsiewa zdegenerowane wartosci
`_pairs_exact_phash` laczylo scene_fingerprints po wartosci BEZ zadnego ograniczenia. Do tej pory uchodzilo, bo zdegenerowanych wartosci bylo malo i blacklista je kasowala. Zmienia to backfill: dokladamy ~240 tys. odciskow z xvideos/xnxx/epornera, a te serwuja zaslepki przy usunietych filmach. Dwa problemy naraz: 1. Blacklista odswieza sie RAZ NA DOBE, wiec swiezo powstala zaslepka ma pelna dobe okna na wpuszczenie do potoku tysiecy niepowiazanych scen. 2. Samozlaczenie jest KWADRATOWE po liczbie scen dzielacych wartosc: zaslepka przy 5 tys. scen to 12,5 mln par i job pada na pamieci, zanim cokolwiek zdazy ja zablacklistowac. Teraz zapytanie samo odsiewa wartosci dzielone przez >=10 scen (ten sam prog co phash_blacklist) ORAZ te juz zablacklistowane, wiec dziala natychmiast, nie po dobie. NIE jest to atrapa: 4% w backfill jedna taka wartosc juz istnieje (12 scen) i blacklista jej jeszcze nie widziala. Po zmianie samozlaczenie daje 23 555 par w 8,3 s.
This commit is contained in:
parent
91c2ce6e0a
commit
276fb4c24d
1 changed files with 26 additions and 1 deletions
|
|
@ -24,6 +24,7 @@ from sqlalchemy.orm import Session
|
|||
|
||||
from app.config import get_settings
|
||||
from app.db import session_scope
|
||||
from app.scheduler.phash_blacklist import DEFAULT_MIN_SCENES
|
||||
from app.models.merge_candidate import MergeCandidate, MergeKind, MergeStatus
|
||||
from app.models.scene import Scene, SceneFingerprint, ScenePerformer
|
||||
from app.resolve.scene_merge import merge_scenes
|
||||
|
|
@ -187,13 +188,37 @@ def _pairs_exact_phash(session: Session) -> Iterable[tuple[uuid.UUID, uuid.UUID]
|
|||
tylko ≥auto_t, więc przypadkowe kolizje z innym performerem/duration wpadną w review).
|
||||
Skalowalne dla setek tys. phashy gdzie Hamming-fuzzy O(N²) jest nierealne."""
|
||||
from sqlalchemy import text
|
||||
|
||||
# Wartości ZDEGENEROWANE (zaślepka, czarna klatka, intro studia) odsiewamy TUTAJ,
|
||||
# nie tylko przez `phash_blacklist`. Dwa powody:
|
||||
#
|
||||
# 1. Blacklista jest odświeżana raz na dobę, więc świeżo wpisana zaślepka ma
|
||||
# całą dobę okna. Do tej pory to nie bolało, ale backfill z sierpnia 2026
|
||||
# dokłada ~240 tys. odcisków z xvideos/xnxx/epornera, a te serwują zaślepki
|
||||
# przy usuniętych filmach. Wystarczy jedna taka wartość, żeby wpuścić do
|
||||
# potoku tysiące scen, których nic nie łączy poza obrazkiem.
|
||||
# 2. To samozłączenie jest KWADRATOWE po liczbie scen dzielących wartość:
|
||||
# zaślepka przy 5 tys. scen to 12,5 mln par, czyli job pada na pamięci,
|
||||
# zanim cokolwiek zdąży ją zablacklistować.
|
||||
#
|
||||
# Próg jest ten sam, co w `phash_blacklist` (≥10 scen = nie identyfikuje sceny),
|
||||
# więc obie ścieżki mówią to samo, tylko ta działa natychmiast.
|
||||
rows = session.execute(text("""
|
||||
WITH degenerate AS (
|
||||
SELECT value
|
||||
FROM scene_fingerprints
|
||||
WHERE kind = 'phash'
|
||||
GROUP BY value
|
||||
HAVING count(DISTINCT scene_id) >= :min_scenes
|
||||
)
|
||||
SELECT DISTINCT a.scene_id AS sa, b.scene_id AS sb
|
||||
FROM scene_fingerprints a
|
||||
JOIN scene_fingerprints b
|
||||
ON a.value = b.value AND a.scene_id < b.scene_id
|
||||
WHERE a.kind = 'phash' AND b.kind = 'phash'
|
||||
"""))
|
||||
AND NOT EXISTS (SELECT 1 FROM degenerate d WHERE d.value = a.value)
|
||||
AND NOT EXISTS (SELECT 1 FROM phash_blacklist b2 WHERE b2.value = a.value)
|
||||
"""), {"min_scenes": DEFAULT_MIN_SCENES})
|
||||
for sa, sb in rows:
|
||||
yield (sa, sb)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue