"""발행 DB를 작업 원장으로 사용해 알림 적재 실패/재시작에도 대상을 다시 찾는다.""" from sqlalchemy import select, or_, text from sqlalchemy.dialects.postgresql import insert from common.database.model.models import sites, places, site_versions, site_search_status as Status from common.enums import SiteStatus def published_conditions(): return ( sites.deleted.is_(False), places.deleted.is_(False), sites.status == SiteStatus.PUBLISHED.value, sites.published_at.is_not(None), sites.current_version_id.is_not(None), ) async def lock_batch(session) -> bool: # 여러 API 프로세스가 같은 크론을 등록해도 외부 호출은 한 곳만 수행한다. return bool(await session.scalar(text("SELECT pg_try_advisory_xact_lock(734920151)"))) async def new_publications(session, property_url: str): result = await session.execute( select(sites, places, site_versions) .join(places, places.place_id == sites.place_id) .join(site_versions, site_versions.site_version_id == sites.current_version_id) .outerjoin(Status, Status.site_id == sites.site_id) .where(*published_conditions(), site_versions.deleted.is_(False), or_( Status.site_id.is_(None), Status.site_version_id != sites.current_version_id, Status.property_url != property_url, Status.published_at != sites.published_at, )) .order_by(sites.published_at).limit(100) ) return result.all() async def register(session, values: dict): reset = dict(values, sitemap_submitted_at=None, inspected_at=None, first_indexed_at=None, inspection=None, error_code=None, failures=0, alerted_at=None, next_check_at=text("now()"), updated_at=text("now()"), deleted=False) await session.execute(insert(Status).values(**values).on_conflict_do_update( index_elements=[Status.site_id], set_=reset, )) async def due_sites(session, property_url: str): result = await session.execute( select(Status).join(sites, sites.site_id == Status.site_id) .join(places, places.place_id == sites.place_id) .where(*published_conditions(), Status.deleted.is_(False), Status.site_version_id == sites.current_version_id, Status.published_at == sites.published_at, Status.property_url == property_url, Status.next_check_at <= text("now()")) .order_by(Status.next_check_at).limit(5) .with_for_update(of=Status) ) return result.scalars().all()