"""야간 dedup 컬럼 재계산 잡 (plan ds-s1-backend-1 B-4 '야간 배치'). duplicate_of / duplicate_count 는 비정규화 캐시다. 문서는 soft-delete only(deleted_at)라 FK ON DELETE SET NULL 이 발화하지 않아, canonical/멤버를 soft-delete 하면 잔여 드리프트가 생긴다(멤버의 stale 포인터·canonical overcount). B-1 업로드 채움은 신규 행만 다루므로, 이 야간 절대 재계산이 전체 정합을 보장한다. 멱등 — 드리프트 없으면 no-op(로그만). 응답 계약(DocumentResponse.duplicate_count/duplicate_of)을 앱(S3)이 읽으므로 정합이 중요. """ import logging from core.database import async_session from services.dedup import reconcile_dedup logger = logging.getLogger("dedup_reconcile") async def run() -> None: try: async with async_session() as session: r = await reconcile_dedup(session, apply=True) if r["changes"]: logger.info( "[dedup_reconcile] groups=%s docs=%s changes=%s applied=%s", r["groups"], r["docs"], r["changes"], r["applied"], ) else: logger.info( "[dedup_reconcile] no drift (groups=%s docs=%s)", r["groups"], r["docs"] ) except Exception: logger.exception("[dedup_reconcile] failed")