LLM WikiAccess-protected knowledge portal

WIKI

labrador-scrapers 라이브러리 크롤러 누락방지 수정 플랜 (DRAFT)

AI Summary Purpose labrador scrapers 모노레포의 라이브러리 크롤러 npm/pypi/conan/vcpkg/hunter 에서 ① 누락 아닌 error→warning ② insert 행별 폴백 ③ 증분/resync 분류 ④ 기타 누락방지를 적용하기 위한 조사 결과 + 수정 플랜. 사용자와 함께 다듬는 중인 DRAFT. Key points 데이터 포맷 절대 불변 모든 제안은 control flow / lo

경로ai/plans/2026-scraper-library-crawler-anti-omission.md
카테고리Project
태그#airflow #anti #crawler #library #omission #project #scraper

# labrador-scrapers 라이브러리 크롤러 누락방지 수정 플랜 (DRAFT)

AI Summary

Purpose:

① 누락 아닌 error→warning ② insert 행별 폴백 ③ 증분/resync 분류 ④ 기타 누락방지를 적용하기 위한 조사 결과 + 수정 플랜. 사용자와 함께 다듬는 중인 DRAFT.

Key points:

skip-logic / WHERE-filter / PROCESSED 정수값(기존 tinyint 컬럼 재사용)만. 컬럼/JSON/ 인코딩/row-shape/PK/SQLModel model-field 변경 0.

npm insert 폴백+증분 skip, pypi 증분 gating.

announce → 메타 404(전파 지연) → 현재 영구 종료(npm processed=5, pypi 4~7 stuck). → golang식 PROCESSED 상태머신(재시도 ladder + grace window)으로 해결.

Relevant when:

Do not read full document unless:

Linked documents:

(grace-window 404=retryable 선례)

검증 결과 (2026-06-23, ladder 설계 전제 — 전부 PASS)

LAST_UPDATED timestamp + EXTRA = "on update CURRENT_TIMESTAMP" → 어떤 UPDATE든 자동 갱신. npm _updatePackageStatus(VO upsert, LAST_UPDATED 미지정)·pypi _update_package_status (SET PROCESSED=:status만) 모두 DB 자동갱신 의존 → PROCESSED만 바꾸면 백오프 게이트 (LAST_UPDATED < NOW()-INTERVAL)가 공짜로 동작. (열린질문 #7 해소.)

스키마 변경 0.**

backlog. (npm 3=버전없음 1.28M, 110=라이선스폭초과 2; pypi 0=43.)

기존 backlog 복구 (ladder는 미래 방지 / 과거 16.3만+12.1만은 별도)

1회 resync로 재검증: npm setProcessedValues([5]) resync / pypi pypi_crawling_resync (resync_status=5). 전파지연 해소된 건 수집(1), 진짜 gone은 다시 5(또는 ladder 도입 후 34). → 코드 변경 없이 기존 resync 모드로 가능(포맷 불변). ladder 배포 후 1회 실행 권장.

스케줄 구성 & 누락 함의 (검증 2026-06-23 — 결정적)

스케줄은 labrador-scrapers가 아니라 ~/labrador/platform/labrador-data-platform Airflow DAG에 있음 (dags/<crawler>_crawler.py). 크롤러별 모드/주기:

크롤러주기모드resync/풀 보완
npm5분(*/5)incremental(npm_package_update+npm_crawling)없음(PROCESSED=5 resync DAG 없음)
pypi30분(*/30)incremental(pypi_crawling_incremental, PROCESSED=0)수동만(pypi_crawling_resync schedule=None)
vcpkg매일 13:00raw=풀, version=증분(버그), product=풀version 보완 없음(수동 풀만)
conan매일 13:00raw/version/product incremental(version=RECORD_UPDATED per-product 게이트=정확)
hunter매일 13:00대부분 풀(--no-incremental, 소형), archive만 증분풀이 보완

함의(이 플랜의 가치 확정):

(npm 162,879 / pypi 120,892, 최근30일 재검증 1.7%/0.3%뿐). → feed-ahead-of-meta ladder(P0)가 활성 누락을 막음(실가치 큼). 기존 backlog는 1회 resync(pypi DAG는 있으나 수동; npm은 resync DAG 자체 없음 → 추가 필요).

(매일 증분이 신규를 잡게 됨). (앞서 "풀이 드레이닝"으로 본 건 수동/일시 풀이었던 것.)

배경 / 범위

- 레거시 ai/labradorlabs/ 포팅: npm_crawler(js/NPM), pypi_crawler(python/PYPI). LIST work-list(PROCESSED) + info_data + baseCralwer(setInsert) = crawler-lib- 계보. - labrador_sqlmodel BaseScraper 3단: conan_crawler, vcpkg_crawler, lib_hunter_crawler. RAW→VERSION→PRODUCT, 프레임워크 ~/labrador/platform/labrador-sqlmodel.

이미 되어 있는 것 (재작업 금지)

bulk UPSERT(ON DUP)→실패 시 이분할(bisect)→단일행 격리→불량행만 drop. ruby _insertChunked 보다 정교. Axis 2 완료(conan/vcpkg/hunter).

(_crawlPackage에서 api−db 버전 diff, resync는 전체 재수집). Axis 2·3 완료.

pypi_crawling(=resync_all, 이름 함정) / pypi_crawling_incremental(processed=0) / pypi_crawling_resync. Axis 3 완료.

★ 핵심 설계: "feed-ahead-of-meta" 영구누락 → golang식 PROCESSED 상태머신

문제 (사용자 발견, npm)

레지스트리에 전파 안 됨** → registry에서 404.

초과면 processed=5(영구 종료). 게다가 processed=4도 다음 run엔 이미 5분 초과 → 또 404면 5. → 5분 안에 메타가 안 올라오면 영구 누락.** (전파 지연은 수분~수시간일 수 있음.)

일반화 (pypi 및 유사)

PROCESSED=4/5/6/7로 빠지면 증분(processed=0만)에서 영영 재수집 안 됨(stuck). 같은 부류.

full ladder. *즉 crawler-lib- 계열엔 이미 검증된 패턴**; 스크래퍼 계열(npm/pypi)에 없음.

설계 원칙

피드/시퀀스는 메타보다 먼저 존재를 알린다(eventual consistency). 따라서 큐에 든 항목의 404는 기본적으로 "아직 없음"이지 "없음"이 아니다. 전파 지연을 넘길 만큼의 시간/재시도 예산 동안 재시도해야 하며, 그 예산을 넘겨 404가 지속될 때만 gone으로 단정한다.

PROCESSED 상태머신 (기존 tinyint 컬럼 재사용 — 포맷 불변, golang 방식)

(LAST_UPDATED 나이로 게이트). claim WHERE = PROCESSED IN (0,30,31,32,33) + (PROCESSED=0 OR LAST_UPDATED < NOW()-INTERVAL <레벨 백오프>). - 예: 30→~15분 후, 31→~1h, 32→~6h, 33→~24h 후 재claim.

증분 재수집 X, resync 재수집 O, 감사 가능. (= 현 5의 자리, 단 "충분히 재시도 후"에만.)

카운트에 넣지 않음(또는 더 높은 cap) — 명백히 gone 아님. 404만 exhaustion으로.

워커 흐름

  1. claim(0,30-33 + 백오프 게이트) → 20(processing) 마킹.
  2. meta fetch:

- 성공 → 수집 → 1 - 버전 없음 → 3 - 404 → 현 ladder 레벨 k<max면 k+1(다음 백오프), k==max면 34(gone) - transient → retryable 유지(30 또는 짧은 백오프), 절대 34 아님

  1. reclaim: 20이 X분 초과 → retryable 복원(크래시 안전, golang _reclaimStuckProcessingEvents).

왜 이게 푸나

시퀀스에 등록됐지만 메타가 전파 지연으로 404인 패키지 → ~1일에 걸쳐 4회 재시도 → 전파 지연 (초~시간)은 그 안에 들어오므로 메타가 뜨는 즉시 수집(1). 진짜 미전파/삭제만 하루+ 지속 → 34(gone)로 명시(조용한 누락 아님). 포맷 불변(PROCESSED 정수값 추가 + 기존 LAST_UPDATED 게이트).

적용 범위

enrichment(코어행 아님)라 후순위. "feed-ahead-of-meta"는 주로 npm/pypi.

수정 항목 (우선순위)

P0 — 진짜 영구누락 차단

  1. npm [구현완료 feature/scraper-lib-anti-omission, 미커밋·미배포]: feed-ahead-of-meta ladder.

getJsonRequestsStatus(404/transient/ok 구분, 기존 getJsonRequests 유지) + ladder 전이 (_nextLadderOn404/_holdLadderOnTransient) + claim WHERE (0,4,30@15m,31@1h,32@6h,33@24h), 34 제외. SINGLE-process(파티션 CRC32 샤딩=disjoint)라 20/reclaim 생략(crash 시 0/30-33로 자연 재수집, 20 추가는 stuck-at-20 신설). py_compile OK, 포맷 불변. 변경=npmCrawler.py + sw/getData.py.

  1. pypi [구현완료, 미커밋·미배포]: ① _upsert_batch raise 제거+행별 폴백(불량행만 skip).

② ladder(_classify_fetch_outcome+_next_ladder_status, http_client get_json_with_detail 활용) + claim WHERE 동일 확장. SINGLE-process 동일 판정(20/reclaim 생략). py_compile OK, 포맷 불변. 변경=pypiCrawler.py. 주의: _upsert_batch 비-raise화는 모든 호출부(version/product/license/ package_list)에 적용(의도된 anti-omission).

  1. conan·vcpkg·hunter [구현완료 e933a99, 미커밋푸시]: git clone/pull 실패 묵살 → stale/빈

트리로 "0건 수집을 성공"으로. → 클론 실패(사용가능 트리 없음)=LabradorScraperCriticalError(abort) + ports/recipes/cmake-projects 비면 critical 가드, pull 실패(기존 트리)=warning+stale 진행.

  1. hunter [구현완료 e933a99]: sort-gen이 한 버전 문자열 실패 시 CriticalError→전 product SORT_ORDER

누락 → per-product catch+warning+return [](스킵), JVM/jar-load 실패만 critical 유지.

  1. hunter [확인완료 e933a99, 코드변경 없음]: RAW⋈ARCHIVE INNER join — archive transform_record

enrichment 실패해도 항상 SHA1 row emit함 확인 → 코드변경 불필요. archive→version 스케줄 순서는 운영.

  1. vcpkg [구현완료 e933a99]: PRODUCT eval(license)json.loads+(JSONDecodeError,TypeError) 폴백.

감사완료: TB_COMP_LIB_VERSION_VCPKG.LICENSE 10,097건 전부 JSON 배열(non-JSON 0) → 동등·byte-identical.

P1 — 로그레벨(Axis 1, 누락 아닌 error→warning, severity만) [구현완료 7903445, 미커밋푸시]

(queueing.py 실제 drop 지점=진짜 유실이라 error 유지 — vcpkg 에이전트 제안은 기각).

+ licenseeUtil(AI/subprocess) enrichment 강등.

npm spdx-empty/product-build/crawl-catch-all/_executeUpsert 행skip/DB open, pypi _upsert_batch 행skip/_insert_ignore/_start_crawling raise/DB open/main critical, setup_iterator critical.

P2 — 효율/증분(누락 아님, 선택)

vs IntegrityError(bisect) 구분.

결정/검증 상태

33→~24h, 이후 34(gone). 총 grace ≈ 1일+4회 재시도. transient(429/5xx/timeout)는 exhaustion 카운트 제외(34로 안 감), 404만 카운트. processing(20)+reclaim 추가(크래시 안전).

남은 열린 질문 (다른 축/크롤러)

- 증분 필터 버그(실재): VCPKGVersionIncrementalCrawlerWHERE RAW.RECORD_UPDATED > query_max(전역 VERSION.RECORD_UPDATED). VERSION max는 매 run 전진 → 그보다 오래된 RAW 변경은 증분에서 영구 제외. (RECORD_UPDATED bump 메커니즘 자체는 정상.) - 정정(원인 점검 2026-06-23): 처음 "누락 43/2,989"로 봤으나 재확인 결과 영구누락이 아니라 풀 run이 드레이닝 중인 lag였음. 점검 중 누락 43→1, 직전 2h 내 version 행 82건 생성(라이브 풀 run 가동). aliyun 3.11.2 수집 확인(byte-exact, RAW=VERSION=3.11.2). 즉 git-walk는 정상(신규버전 수집됨), aliyun(RAW 2025-02-02)은 버그난 증분으론 선택 불가한데 수집됨 = VCPKGVersionCrawler가 backlog 흡수. - 결론: 증분-only 운영이면 누락(증분 버그)이지만, 풀 run 주기 실행으로 실제론 완화됨. git-walk 최신commit 누락 우려(원래 #2)는 기각. - 수정 [구현완료 39a8d35]: conan식 per-product RECORD_UPDATED 비교를 시도했으나 vcpkg 에선 version 행 RECORD_UPDATED 가 재처리로 따로 전진해 0건 선택 → 실패. 직접 신호인 '(PRODUCT_KEY, VERSION) 미존재'로 교체(VCPKGVersionIncrementalCrawler.setup_iterator: RAW LEFT JOIN VERSION ON PRODUCT_KEY=PATH AND VERSION WHERE raw.VERSION IS NOT NULL AND v.VERSION IS NULL). 선택 포트는 scrape 시 git 히스토리 재walk → 현재+누락 버전 수집, 현재버전 수집된 포트는 제외(수렴, churn 없음). VERSION 부재 시 전체 RAW 폴백. 검증: 쿼리 read-only로 누락 스냅샷 정확 선택, py_compile OK, 쿼리만(포맷 불변). 교훈: RECORD_UPDATED-기반 증분은 derived 테이블 행이 재처리로 touch되면 신뢰 불가 — '현재값 미수집' 직접 비교가 안전. - 수정 위상(정정 반영): 활성 손실 차단이 아니라 증분 모드를 omission-safe하게 만드는 것 = 비싼 풀 run 의존을 줄이고 증분만으로 lag를 작게 유지. 풀 run이 이미 backlog를 흡수하므로 별도 일괄 복구는 불필요(풀이 돌고 있음). git-walk 정상 확인됐으므로 "최신 commit 못 잡음" 우려도 해소.

각 크롤러 main.py/ScraperRunner는 복사본이라 runner/로그는 크롤러별 따로.

데이터 포맷 안전 원칙 (모든 항목 공통)

스케줄/scraper-mode, 예외 propagation(abort vs skip), 기존 LAST_UPDATED 활용.

npm PATH/LICENSE 컬럼 폭은 절대 확장 금지(None-on-overflow가 포맷 계약).

진행 방식