# 라이브러리 누락(missing-data) 개선 작업 — repo별 정리
AI Summary
Purpose:
- 2026-06 ~ 2026-07 초(약 1.5개월) 동안 진행한 라이브러리 크롤러 누락 개선 작업을 라이브러리 repo별로 표로 정리.
- 2026-07 live Jira/Confluence audit confirmed this was also completed as a 12-ecosystem documentation package: onboarding guide + improvement-history page per ecosystem.
- "무엇이 왜 누락됐고, 크롤러/도구를 어떻게 고쳤고, 결과가 무엇인지"를 한눈에.
Key points:
- 공통 방법론: finder(누락 후보 추출) → registry 실검증 → 크롤러 근본수정 → 백필 → excluded 목록화.
- 핵심 원칙: DB-대-DB 단순비교 금지. 권위 레지스트리(proxy/list.json/catalog/git 등)로 실검증해야 함 (Go에서 "61.7만 누락"이 실제 1,369였던 교훈).
- 근본 원인은 대부분 "소스에 없음"이 아니라 크롤러 결함: transient 실패를 완료로 마킹, all-or-nothing 페이지, feed-ahead-of-meta(피드가 메타보다 앞섬), 대문자 버전 escape 누락, PROCESSED 작업큐 부재, 스키마 split 오인 등.
- 데이터 포맷 불변 원칙: 백필/수정은 컬럼·JSON 인코딩을 크롤러와 byte-호환으로 유지, 상세행 클로버(ON DUP KEY로 null 덮어쓰기) 방지.
Relevant when:
- 특정 라이브러리 크롤러의 누락 개선 이력/근거가 필요할 때, 회귀 조사, 포트폴리오/보고 정리.
Linked documents:
ai/wiki/projects/library-missing-product-audit.md(product 단위 전수감사 — 규모 정량화)ai/repo-notes/crawler-lib-golang.md,ai/repo-notes/crawler-lib-java.md,ai/repo-notes/crawler-lib-ruby.md,ai/repo-notes/labrador-scrapers.md- 원 출처 worklog:
ai/worklog/2026/2026-W24.md~2026-W27.md - Live Jira/Confluence audit:
ai/sources/live-jira-confluence-onboarding-portfolio-audit-2026-07-07.md
Open Questions
- 일부 도구(
~/labrador/tool/*_missing_products)는 미커밋 로컬 스크립트. 커밋/공유 여부 미정. - 하드코딩 GitHub PAT(여러 크롤러
util/licenseeUtil.py)는 별건 보안 이슈로 미조치.
공통 패턴 (모든 repo 공유)
| 패턴 | 내용 |
|---|---|
| finder 도구 세트 | ~/labrador/tool/<eco>_missing_products — raw diff → registry 실검증 → missing_real.tsv + excluded.tsv + summary.md, 날짜 디렉터리(YYYY-MM-DD/) resumable |
| excluded 자기치유 | gone-404 / zero-version / row-skip을 *_excluded.txt에 축적, 다음 실행 시 registry로 재검증해 부활분 자동 해제 |
| PROCESSED 작업큐 | go식 상태머신 0 대기 / 20~24 처리중 / 30~34(또는 30~38) 재시도 ladder / 1 완료 / 5 not_found / 3 no-version |
| retry ladder(backoff) | 재시도를 run 간격으로 분산(15m/1h/6h/24h 등), 소진 시 terminal 상태로 포기 — 무한 재시도/조용한 누락 방지 |
| 로그레벨 정책 | 실제 유실/영구누락 지점만 error, transient(재시도 가능)·행별 폴백·저수준 DB는 warning |
| DB 타깃 규칙 | 수집=211.115.125.165:43316(165)만. 과거 shared .env가 164를 가리켜 오적재한 사고 후 ai/workspace/crawler-db-target.md로 못박음 |
| 스키마 split 주의 | product=labradordb인 생태계(npm/pypi/php/ruby/swift/maven)와 전용 gatheringdb 테이블 생태계(go/conan/vcpkg/hunter/spm) 혼동 시 "0건 누락" 오판 |
repo별 개선
crawler-lib-golang (Go modules)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-18 | 워커 전원 queue-claim 락 타임아웃 스팸. 죽은 홀더의 named lock이 wait_timeout 8h 동안 미해제 | indexer↔worker 인터리브 데몬화(빈 큐=idle sleep, main만 index), GO_IDLE_SLEEP_SECONDS | 락 물림 회피 구조 | feature/go-crawler-indexer-loop(당시 미머지) |
| 06-18 | "61.7만 누락(gpnf)" 과대보고 — index path를 PRODUCT_KEY처럼 단순비교 | analyze_gpnf.py로 전수 분해(A/B/C/D/E 버킷) | 진짜 없음 0, 대부분 declared_diff/subpath/case/큐 backlog | 정량 확인 |
| 06-18~19 | pending 366,798이 큐 대기로만 남아 gone 판정 불가 | proxy.golang.org 실검증 + backfill_pending.py(dataCrawlingV2 재현) | 신규 version 1,565,267 수집, product +294,792(1.92M→2.22M), gpnf 해소 39.9%→99.3%. DB write 버그 3종 수정(prepared stmt / DESC 65KB 절단 / epoch time None), LATEST_VERSION 전체정렬 최신 보정 | 백필 완료 |
| 06-22 | 실제 누락 1,369(LIST_V2 밖 proxy-exists)은 pending 백필로 못 잡음 | backfill_missing_real.py로 직접수집 | collected 1,348 + alias, unresolved/err 0 | 완료 |
| 07-01 | 대문자 포함 버전(-SNAPSHOT/-RC1 등)의 .mod/.info fetch가 escape 누락으로 404 → LIST_V2 PROCESSED=0 영구 정체 = 조용한 누락 45,508건 | component.py 3개 proxy fetch에 escape_go_module_proxy_path(version) 적용 + 회귀 테스트 | 대문자 버전 45K 재큐(터미널 상태 리셋) | crawler 54cf534 (push) |
| 07-01 | 전역 GET_LOCK claim이 워커 직렬화 → starvation | _claimV2Batch를 SELECT … FOR UPDATE SKIP LOCKED로 재작성 | disjoint 병렬 claim, 스팸 제거 | crawler 83882ac (push) |
| 07-01 | 제외목록 정의 혼선 | golang_excluded.tsv = "index path ≠ product_key"만 담도록 재정의 + finalize_lists.py 영구화 | 236,506 + proxy_gone | 완료 |
repo-note: ai/repo-notes/crawler-lib-golang.md. 상태머신: 0 pending / 1 complete / 5 not_found / 6 alias / 20~29 processing / 30~38 retry / 39 exhausted.
crawler-lib-java (Maven Central)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06 초 | Java(Spring/MyBatis) 구현의 omission 이력 + 유지난이 | Python 재작성(발견=인덱스 소비+스켈레톤, 보강=POM 메타 분리, index-helper) | 재작성 완료 | branch → master |
| 06-16 | not_found 146건 원인 = collect가 버전을 maven-metadata.xml 하나에만 의존(404/플러그인 메타면 통째 스킵) + 비표준 파일명 404 | 디렉터리 리스팅 폴백(fetch_versions_from_listing) + .pom/.sha1 파일명 리스팅 재시도 | 102 product 복구(A 97/B 43 gone/…). upsert 클로버 방지 안전절차(thin은 INSERT IGNORE) | tool 미커밋 |
| 06-16 | version 단위 누락 미측정 + SHA1 결손 47.5% | extract_maven_missing_versions.py(1,045,271 누락) + backfill_sha1_from_index.py(로컬 인덱스 캐시, HTTP 0, AND SHA1_VALUE IS NULL 가드) | 버전 백필 + SHA1 보정 | 진행/완료 |
| 06-23 | manageNewAddedVersion이 어떤 예외든 PROCESSED=1로 마킹 → name/license/deps 빈 채 영구 누락(메인 omission). antcloud류 transient 무한재시도 | crawlVersion transient raise 제거 → version+sha1로 pending(90) 반환, run당 pending 재수집 1패스 ladder(90→91~97→98) | 무한재시도 제거, 누락 pending 전환 | master b37eea3 |
| 06-23 | 아카이브 루프가 POM 네트워크에 막혀 누락 가능 | 스켈레톤-only 아카이브 루프(network 0, version 스켈레톤 90 + product compare-and-set) + insertAllProductsFromArchive 1052 모호컬럼 수정 | 구조적으로 아카이브 누락 불가 | master 264a650, e4c19d5 |
| 06-24 | 단일 스레드 POM 보강 ~500/hr, 백로그 1.26M+ (수개월) | indexer↔meta-worker 분리 + CRC32 샤딩(main_all/main_worker, 89=claim) | 분산 드레인 설계(코드 병합) | feature/maven-metaworker-split |
repo-note: ai/repo-notes/crawler-lib-java.md. 배포 경로는 단일 프로세스 run()(90/91~97/98), 샤딩(89)은 별도.
crawler-lib-dotnet (NuGet)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-17 | LIST pending(PROCESSED=0)이 10일간 안 줆. all-or-nothing 페이지 + leaf fetch가 재시도 없는 requests.get 1회 → leaf 1건 transient 실패마다 페이지 전체 requeue | getJsonRequests(retries=3, backoff) — transient만 2/4/6s 재시도, 404/410 즉시 None | stuck 24개 중 일시성분 해소(도커 검증) | tool/crawler 수정 |
| 06-18 | version 단위 누락 미측정 | extract_nuget_missing_versions.py(catalog page inline 활용, leaf fetch 불필요) + version 대소문자 버그 수정(대문자 버전 거짓누락 제거) | 보정 누락 351,314 / 68,448 패키지 | tool 미커밋 |
| 06-18 | — | backfill_nuget_versions.py 안전 2단계(file→done seed→누락분만 both) | 상세완료 351,304 / err 0 (~34h) | 완료 |
| 06-23 | 증분이 기존 버전을 재수집 | 기존 버전 재수집 → resync로 분리 | 증분 부하 감소 | master 324b277 |
| W26 | 배치 insert가 1행 불량에 전체 롤백 | _insertChunked 행별 폴백 | 손실 최소화 | master a1aafbc |
audit: catalog universe 817,073 vs DB → 진짜 누락 24.
crawler-lib-php (Packagist / Composer)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06 초 | 누락 조사 + product/version finder 부재 | missing investigation + finder + extract_composer_missing_versions.py(p2/{name}.json 전수) | version 누락 718,431 / 91,322 패키지 측정 | tool 미커밋 |
| 06-16~17 | — | 165 백필 + LATEST_VERSION stable 보정(dev를 latest로 넣던 것 → stable 최신) | pkg 90,649 / version 2.33M 적재, LATEST 보정 11,878 | 완료. ⚠ 164 오타깃 사고 정정 → 165 규칙 확립 |
| 06-23~07-01 | feed-ahead-of-meta: Packagist 피드가 버전 게시 전에 크롤 → 200·zero-version을 완료(1)로 굳혀 증분이 다시 안 봄 → 영구 누락 | zero-version 재시도 ladder(0→30→31→32→33→34→35 포기, 대기 1h/5h/10h/16h/24h) util/ladder.py | 재발 방지 + 8건 백필 | master 9d5cf76 |
| 07-01 | all-dev 패키지에서 dependabot 브랜치가 버전으로 적재(노이즈) | util/dev_versions.py로 default-branch dev만 유지 | docuseal 등 노이즈 정리 | master 5a6792d |
audit: list.json 451,876 vs DB → 진짜 누락 9,490(가장 큰 갭).
crawler-lib-ruby (RubyGems)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-09 | list PROCESSED를 항상 1 하드코딩(피드=즉시크롤, 컬럼 死) → 재시도/backlog 관리 불가 | go식 작업큐 전환(ruby_status.py: 0/20~24/30~34/1/5/3), enqueue/claim/reclaim/advance | 재시도·backlog 관리 가능 | master 6d9a0f4 |
| 06-09 | libraries.io 의존 + eval 파싱 깨짐 + JVM 버전정렬 의존 + LICENSE_IDS varchar(32) 초과 거부 | 라이선스 체인 재구성(rubygems→licensee→github, libraries.io 제거), eval→json.loads/ast, JVM→순수파이썬 versionSort, fitLicenseIds(32자 맞춤) | 유실 지점 제거 | master f474dc5 |
| 06-09 | 신규버전 0건 gem이 오판→재시도 고착 | 성공판정을 list 기록이 아닌 version 행 존재로 변경 | 오판 해소 | master 0f435d2 |
| 06-09 | 일 1회 스케줄 | 3시간 간격 8회/일 | 지연 감소 | master 3086907 |
| 06-16 | version 단위 누락 | finder + 백필 | version 68,755 upsert, LATEST 보정 77 | 완료 |
audit: /names 193,954 vs DB → 진짜 누락 1(shmactor).
crawler-lib-swift (CocoaPods)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-09 | 누락 682 = version/product 전무인데 Specs엔 존재 = 크롤러 버그. ① 매 실행 Specs 전체 재clone + 단발 git pull(재시도 없음) → 부분/타임아웃 실패 시 최근 pod 누락 ② 전체 단일 try/except(1개 예외로 회차 중단) ③ product는 최신버전에서만 생성 | ① rm -rf 제거 → 체크아웃 유지·증분 pull + 3회 재시도 ② per-pod try/except 격리 ③ version 있는데 product 없으면 보강 (포맷 불변) | 685 pod 백필(version 2,338, 685/685 product) | cocoapods.py만 변경 |
| 06-09 | finder 부재 | swift_missing_products(all_pods.txt universe) + backfill collector + excluded | 누락 685 → 백필 | tool 미커밋 |
audit: all_pods.txt 102,509 vs DB → 진짜 누락 3.
labrador-scrapers / spm_scraper (Swift Package Index)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-24 (W26) | 누락 포인트 미분석 + 고아 product | 누락 체크 도구 + 버전 백필 + 태그유무 분류 + 고아 복구 | 재탐지 21건 분류→백필 | DAT-3254 |
| 06-24 | ?after= 페이지네이션 조용한 절단(throttle 시 버전 누락) + 증분 부재 | 누락방안 수정 + 증분 크롤링 도입 | 리뷰 반영 3건 | 64ed39c |
| 06-24 | RELEASE_DATE가 버전 무관하게 동일 | 버전별 실제 git 태그 일시로 수정 | 정확한 릴리스일 | 3e74f36 |
| 07-01 | swiftpackageindex.com SSL CERTIFICATE_VERIFY_FAILED(cloudscraper가 커스텀 SSLContext에서 certifi 미탑재) | 공용 httpClient(certifi 명시) 재사용으로 detail_parser fetch 대체 | SSL 오류 해소(배포 대기) | scrapers 8d700ce |
| 07-01 | 스케줄이 매일 전체 resync → SSL 에러 최대 노출 | 증분(월~토)/전체(일) DAG 분리, 증분 기본 on | 부하·에러 최소화 | platform 46e876f |
| 07-01 | 증분이 product LAST_UPDATED(churn)로 판단 → 절대 skip 안 됨(전체 재수집) | LIST 테이블(TB_COMP_LIB_SPM_LIST) 기반 증분 부활(PROCESSED=1 skip) + clone stall(--filter=blob:none, timeout) + 중복키(1062) upsert 수정 | 증분 정상화 | scrapers fe4a9a6 |
| 07-01 | 증분 전환 회귀: Vulnerability crawler __init__ TypeError | (self, target_db, **kwargs) 시그니처 수정 | 회귀 해소 | scrapers 67d3736 |
audit: PackageList 11,166 vs DB → 진짜 누락 47(일부 tagless). ⚠ tagless repo는 version PK가 VERSION NOT NULL이라 영구 사각지대.
labrador-scrapers / npm_crawler
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-09 | finder/excluded 부재 | npm_missing_products finder + backfill collector + npm_excluded.txt | 노이즈 제거 후 actionable 62 | tool 미커밋 |
| 06-29 | scoped(@scope/name) 패키지 대량 누락 162,563(99.94%) | 원인 진단·크롤러 수정·백필 | 대량 백필 | W26 기록 |
| 07-01 | "누락" CSV 13건 = 실제 누락 0. tryversion의 top-level name 오염(tryveraasion)을 그대로 저장 = NAME 오염 크롤러 버그(광역) | getVersionVo NAME 소스를 top-level name → 정규 슬러그(product["path"])로 변경(TDD) | 이름기준 누락 false positive 원천 제거 | scrapers 7527094 |
| 06-23 | feed-ahead-of-meta | pypi와 공용 ladder 구현 | 재발 방지 | (아래 pypi 참조) |
DB: PRODUCT_KEY=AES-CBC(key=IV=NPMNPMNPMNPMNPMN). PROCESSED 5(stuck) 재검증 미스케줄 이슈 존재.
labrador-scrapers / pypi_crawler
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-23 | feed-ahead-of-meta(신규 패키지가 메타 전파 전 크롤) | npm과 함께 retry ladder(0/30~33) 구현 + Docker 검증 | 재발 방지 | 미커밋→커밋 |
| 06-24 | [total summary] 로그 ×2 중복 + SQLAlchemy echo 소음 | 전용 로거/print()로 재해결 + echo 억제 | 모니터링 파싱 일관성 | W26 |
참고: npm/pypi는 증분만+PROCESSED=5 재검증 미스케줄 → 404→5 누락 실누적(npm 162,879 / pypi 120,892, 최근30일 재검증 1.7%/0.3%).
labrador-scrapers / conan · vcpkg · lib_hunter_crawler (C/C++)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 06-23 | vcpkg 증분이 신규버전 누락(git-log 반영 안 됨) | 증분 omission 수정(setup_iterator RAW LEFT JOIN VERSION 미수집분) | 43/2,989 신규버전 누락 해소 | 39a8d35 |
| 06-23 | conan 스키마가 vcpkg와 불일치 | conan 테이블을 vcpkg 포맷으로 정렬(cutover) | 통일 | 대기 |
| 06-23 | C/C++ 누락 도구 부재 | conan/vcpkg/hunter 누락 탐지+백필 도구 신설 + 운영DB 백필 | 전수감사 누락 0 | 완료 |
| 07-01 | conan "누락" 25건 = PRODUCT_CONAN엔 있으나 공용 TB_COMP_LIB_PRODUCT 미전파 | 전파 진단·백필 | 25건 처리 | W27 |
audit: conan 0 / hunter 0 / vcpkg 0 (완전 동기화). ⚠ conan LATEST_VERSION 역전(version SORT_ORDER=0=최신 vs product MAX(SORT_ORDER)=오래된 것) 코드 확인.
labrador-scrapers / comp_lib_vuln_processor (누락 개선 연관)
| 시기 | 문제 / 원인 | 개선 | 결과 | 커밋·상태 |
|---|---|---|---|---|
| 07-01 | go product가 gatheringdb 전용 테이블로 이관됐는데 vuln processor는 옛 labradordb.TB_COMP_LIB_PRODUCT에 씀 → 신규 go product가 취약점 enrichment 못 받음 | 단일 리졸버 resolve_comp_lib_product_table 도입(golang→PRODUCT_GOLANG 등) | go vuln 라우팅 정정 | scrapers 05a76da |
| 07-02 | golang PRODUCT_KEY(as_cs) ↔ vuln(ai_ci) JOIN 1267 collation 오류로 DAG pod 죽음 | JOIN에 COLLATE utf8mb4_0900_as_cs 통일(collation.py) | 1267 해소(라이브 검증) | scrapers 1322f9a |
전수감사 요약 (product 단위, 2026-06-24 기준)
상세는
ai/wiki/projects/library-missing-product-audit.md.
| 생태계 | product 총수 | 진짜 누락(real_missing) |
|---|---|---|
| golang | 1.93M | 1,369 (수집완료) |
| composer | 451,876 | 9,490 (최대 갭) |
| maven | 838,155 | 1,689 |
| spm | 11,166 | 47 |
| nuget | 817,073 | 24 |
| swift(cocoapods) | 102,509 | 3 |
| rubygems | 193,954 | 1 |
| conan / hunter / vcpkg | 1,929 / 547 / — | 0 / 0 / 0 |
- 미수집 합계(golang 제외): 11,254. 대부분 생태계는 사실상 동기화(≤47).
- 여러 생태계에서 DB-only(레지스트리엔 없는데 DB엔 있음 = yanked/삭제/과수집) 가 누락보다 큼(composer 73,607 · rubygems 27,751 · nuget 10,256 등) → stale 정리가 별도 과제.