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.config import get_settings
|
||||||
from app.db import session_scope
|
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.merge_candidate import MergeCandidate, MergeKind, MergeStatus
|
||||||
from app.models.scene import Scene, SceneFingerprint, ScenePerformer
|
from app.models.scene import Scene, SceneFingerprint, ScenePerformer
|
||||||
from app.resolve.scene_merge import merge_scenes
|
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).
|
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."""
|
Skalowalne dla setek tys. phashy gdzie Hamming-fuzzy O(N²) jest nierealne."""
|
||||||
from sqlalchemy import text
|
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("""
|
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
|
SELECT DISTINCT a.scene_id AS sa, b.scene_id AS sb
|
||||||
FROM scene_fingerprints a
|
FROM scene_fingerprints a
|
||||||
JOIN scene_fingerprints b
|
JOIN scene_fingerprints b
|
||||||
ON a.value = b.value AND a.scene_id < b.scene_id
|
ON a.value = b.value AND a.scene_id < b.scene_id
|
||||||
WHERE a.kind = 'phash' AND b.kind = 'phash'
|
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:
|
for sa, sb in rows:
|
||||||
yield (sa, sb)
|
yield (sa, sb)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue