virtual-insanity
← 리포트 목록

외사 리서치 수집 파이프라인 구조 재설계

2026-08-12 codex

외사 리서치 수집 파이프라인 구조 재설계

1. 이번 설계의 기준

  • 목적: 매일 05:45 수집이 한 번 빠져도, 놓친 리포트를 나중에 회수하고, 무엇을 놓쳤는지 사후에 증명하고, 브라우저 장애가 5개 하우스 동시 장애로 번지지 않게 만드는 것.
  • 절대 유지할 것
  • 골드만 요청 상한 유지
  • blocked.json 유지
  • 자동 로그인 확대 금지
  • 외부 재배포 금지
  • 용어
  • API(Application Programming Interface): 브라우저 대신 서비스가 제공하는 공식 호출 방식
  • CDP(Chrome DevTools Protocol): 크롬을 원격 조종하는 디버그 연결 방식
  • JSONL(JSON Lines): 문서 1건당 한 줄씩 적는 텍스트 형식. 부분 확인과 압축이 쉬움

2. 현재 코드와 상태에서 확인한 사실

2-1. 현재 구조

  • 오케스트레이터: scripts/collect_daily_archive.py
  • 저장 계층: scripts/archive_common.py
  • 분류 규칙: scripts/report_routing.py
  • 브라우저 세션 관리: scripts/broker_session_manager.py
  • 세션 워머: scripts/warm_broker_sessions.py
  • 실행 흔적 저장: runs/<날짜>/<하우스>/...
  • 전역 보관 색인: data/archive_index.json

2-2. 2026-08-12 현재 실측

  • data/archive_index.json 항목 수: 1,330건
  • data/archive_index.json 크기: 약 2.38MB
  • 저장소 사용량
  • raw/: 약 808MB
  • runs/: 약 547MB
  • data/: 약 3.7MB
  • runs/20260812/ 기준 목록 아티팩트 크기
  • 노무라 listing-054550.json: 약 1.96MB
  • HSBC listing-054609.json: 약 30KB
  • 나머지 6곳 portal-latest.json: 249B ~ 1.1KB 수준 요약본

2-3. 결함 A 관련 추가 확인

  • HSBC는 현재 코드가 날짜 조건을 보내지 않음. RowsPerPage/MaxNumberOfRows만 바꿈.
  • 2026-08-12 읽기 전용 확인 결과
  • rows=100 → 100건, 가장 오래된 발행일 2026-08-10
  • rows=150 → 150건, 가장 오래된 발행일 2026-08-07
  • rows=200 → 200건, 가장 오래된 발행일 2026-08-07
  • rows=300 → 300건, 가장 오래된 발행일 2026-08-06
  • 같은 확인에서 includepaging=true, page=2, start=101, offset=100은 응답 건수·날짜 범위를 바꾸지 못함.
  • BofA는 현재 코드상 날짜·페이지 인자를 쓰지 않음. 다만 현재 열린 https://markets.ml.com/high-grade 탭 안에
  • Advanced Search 링크(/researchlibrary/advancedsearch)
  • searchKey
  • 숨은 formDate
  • HTML/PDF 리소스 URL hidden field 가 보임. 즉 검색 UI 흔적은 있으나, 실제 날짜/페이지 파라미터는 이번 확인에서 확정 못 함.
  • 모간스탠리는 현재 코드가 홈 카드와 MorningSummary만 읽음.
  • 인증된 portal-content-service/content/Home?... 응답: 200, 섹션 9개
  • 인증된 portal-content-service/content/auto/Home?... 응답: 200, 섹션 4개
  • 카드 수는 Home 2/5/0/7/4/7/3/3/7, MorningSummary 6개 수준
  • 홈 콘텐츠 API는 확인됐지만 날짜/페이지 검색 API는 이번 확인에서 확정 못 함.

2-4. 결함 C 관련 추가 확인

  • 현재 broker_session_manager.py는 여전히
  • 씨티, 모간스탠리, 골드만, TD, 바클레이즈를 포트 9227 / 프로필 citi-velocity에 묶음
  • BofA만 9233 전용
  • 2026-08-12 현재 프로필 크기
  • citi-velocity: 3.0GB
  • goldman-marquee: 1.5GB
  • bofa-markets: 165MB
  • hsbc-research: 136MB
  • nomura-now: 163MB
  • matrix-ms: 12MB
  • citi-velocity 내부 큰 항목
  • Default/Cache: 951MB
  • Default/Extensions: 1.2GB
  • Default/Code Cache: 300MB
  • Default/Service Worker: 170MB
  • 가장 큰 확장 폴더는 366MB, 329MB 두 개가 확인됨
  • warm_broker_sessions.py는 현재 탭/웹소켓 상태 점검과 알림만 하고 재시작은 하지 않음

3. 결함 A 설계안 — 백필 능력의 명시화와 자동 재시도 구조

3-1. 설계 목표

  1. 하우스별로 지금 코드가 실제로 가능한 백필 범위와 포털 잠재 능력의 확인 상태를 분리해 기록
  2. 백필 요청이 능력을 넘으면 최근 것만 다시 받아오는 식의 묵시적 동작 금지
  3. 실패 날짜를 자동 적재하고, 가능한 하우스만 다음 실행에서 자동 재시도
  4. HSBC·BofA·모간스탠리는 “가능성 탐색”과 “운영용 백필”을 분리

3-2. 제안 데이터 구조

A. 하우스별 능력 레지스트리

신규 파일: data/source_capabilities.json

{
  "version": 1,
  "updated_at": "2026-08-12T00:00:00+09:00",
  "sources": {
    "nomura": {
      "collector_mode": "api_direct",
      "backfill_contract": {
        "status": "supported",
        "date_filter": "direct_range",
        "pagination": "offset",
        "page_size_confirmed": 100,
        "max_pages_confirmed": 10,
        "implemented_lookback_days": null,
        "effective_result_ceiling": 1000,
        "warning_on_exceed": "result_ceiling"
      },
      "portal_capability_status": "confirmed"
    },
    "hsbc": {
      "collector_mode": "http_cookie_reuse",
      "backfill_contract": {
        "status": "partial",
        "date_filter": "none",
        "pagination": "not_confirmed",
        "page_size_confirmed": 300,
        "implemented_lookback_days": null,
        "observed_oldest_publication_by_page_size": {
          "100": "2026-08-10",
          "150": "2026-08-07",
          "200": "2026-08-07",
          "300": "2026-08-06"
        },
        "warning_on_exceed": "recent_count_only"
      },
      "portal_capability_status": "partially_confirmed"
    }
  }
}

핵심은 implemented_lookback_days만 두지 말고 아래를 같이 둬야 한다는 점임.

  • status: supported | partial | unsupported
  • date_filter: direct_range | fixed_window | none | home_only
  • pagination: offset | page_number | cursor | none | not_confirmed
  • implemented_lookback_days: 현재 코드가 실제 보장하는 범위
  • portal_capability_status: confirmed | partially_confirmed | unknown
  • warning_on_exceed: 초과 요청 시 어떤 경고를 낼지

B. 백필 대기열

신규 파일: data/backfill_queue.json

{
  "version": 1,
  "items": [
    {
      "id": "hsbc:2026-08-03:2026-08-06",
      "source": "hsbc",
      "requested_window": {"start": "2026-08-03", "end": "2026-08-06"},
      "reason": "daily_collection_failed_or_missing",
      "priority": "critical",
      "capability_assessment": "unsupported_recent_count_only",
      "status": "pending_warning",
      "attempt_count": 0,
      "last_result": null,
      "created_at": "2026-08-12T05:46:00+09:00"
    }
  ]
}

상태값

  • pending: 다음 실행에서 자동 재시도 가능
  • pending_warning: 자동 재시도하면 오해를 낳으므로 먼저 경고만 내야 함
  • running
  • resolved
  • blocked_capability_gap: 포털/코드 능력 부족으로 자동 회수 불가
  • manual_hold

3-3. 제안 인터페이스

A. 백필 판정 인터페이스

신규 모듈 제안: scripts/archive_capabilities.py

  • assess_backfill(source, start_date, end_date) -> assessment
  • 반환값 예시
{
  "source": "hsbc",
  "requested_window": {"start": "2026-08-03", "end": "2026-08-06"},
  "status": "unsupported",
  "effective_window": null,
  "warning_code": "recent_count_only",
  "message": "HSBC 현재 수집기는 날짜 조건 없이 최신 일부만 조회함. 2026-08-03~2026-08-06 백필 보장 불가"
}

B. 수집기 반환 계약 확장

각 collect_<house>_archive.py의 collect() 결과에 아래 필드를 추가하는 설계를 권장함.

  • requested_window
  • effective_window
  • capability_status
  • warnings
  • discovery_artifact

초과 요청인데 능력 밖이면 수집 자체를 unsupported_backfill로 종료해야 함. 기본 동작으로 최근 목록 재수집 금지.

3-4. 자동 재시도 흐름

  1. collect_daily_archive.py가 일일 수집 수행
  2. 하우스 실패, 일부 문서 실패, 또는 목록 대비 저장 누락이 있으면 backfill_queue.json에 적재
  3. 다음 실행에서
  4. 먼저 당일 수집 수행
  5. 그 다음 backfill_queue.json에서 pending만 제한적으로 수행
  6. pending_warning과 blocked_capability_gap은 자동 수집 대신 실행 결과 요약에 정확한 날짜를 적은 경고로 남김

권장 이유

  • 당일 05:45 수집이 최우선임
  • 백필이 골드만 상한, 브라우저 부하, 차단 리스크를 밀어내면 안 됨

3-5. 하우스별 설계 판단

노무라

  • 그대로 백필 주력 소스로 둠
  • 단, 1000건 상한을 능력 레지스트리에 명시
  • 요청 날짜 범위는 넓어도 결과 1000건을 넘기면 “미회수 가능 구간 있음” 경고 필요

바클레이즈

  • 현재 설계상 14일 보장으로 명시
  • 14일 초과 요청은
  • 최근 14일은 실행
  • 나머지 구간은 blocked_capability_gap으로 분리 기록
  • “일부만 회수됨”을 명시해야 함

골드만 / TD

  • 현재 구현은 14일 컷오프이므로 일단 능력 레지스트리에 현행 구현 14일로 명시
  • 다만 이것이 포털 한계인지 코드 한계인지는 분리 표기
  • implemented_lookback_days: 14
  • portal_capability_status: unknown
  • 이렇게 해 두면 나중에 코드 수정만으로 넓힐 수 있는지 판단이 쉬움

씨티

  • 현재는 landing + publication feed(21일)의 혼합 구조
  • 능력 레지스트리도 하우스 단위가 아니라 채널 단위를 허용해야 함
  • landing_home: 홈 최신 카드만
  • publication_feed: 21일, cursor 미구현
  • 백필 요청 시 “이 문서가 feed 대상 시리즈인지”에 따라 지원 여부가 갈림

HSBC

  • 즉시 적용 권장안: 운영 기본 rows=100을 바로 올리지 말고, 검증 후 단계 상향
  • 이번 확인으로 rows=300까지는 서버가 응답을 받아 줌
  • 따라서 레지스트리에
  • page_size_confirmed: 300
  • pagination: not_confirmed
  • warning_on_exceed: recent_count_only 를 먼저 기록
  • 다음 단계 조사 경로
  • rows=500, rows=1000 단발 검증
  • 응답 시간·HTML 크기·중복률 측정
  • 가장 오래된 발행일이 실제로 얼마나 넓어지는지 확인
  • includepaging=true, page=2, start, offset은 이번 확인에서 효과 없었으므로 운영 설계에는 넣지 않음

BofA

  • 현재 collector 능력은 unsupported로 두는 것이 안전함
  • 이유: 날짜/페이지를 쓰지 않고, 초과 요청 시 최근 것만 다시 읽게 됨
  • 다음 조사 경로는 명확함
  • 이미 보이는 /researchlibrary/advancedsearch 우선 확인
  • searchKey, formDate, hidden resource URL을 쓰는 실제 요청을 네트워크에서 캡처
  • 검색 요청에 날짜/페이지 인자가 붙는지 확인
  • 이번 실행에서 실제 날짜/페이지 파라미터는 확인 못 함

모간스탠리

  • 현재 collector 능력은 unsupported 또는 partial_home_only
  • 이유: 홈/모닝서머리 카드만 확인됐고 검색 API는 확정 못 함
  • 다음 조사 경로
  • 홈에서 실제 검색·필터 조작 시 호출되는 API 캡처
  • 현재 확인된 portal-content-service가 홈 고정 카드용인지, 검색에도 재사용되는지 분리 확인
  • scripts/probe_ms_search.py는 후보 탐침 흔적일 뿐, 운영 근거로 쓰지 않음
  • 이번 실행에서 검색용 날짜/페이지 API는 확인 못 함

3-6. 이 설계의 위험과 트레이드오프

  • 장점
  • “백필 가능/불가”가 하우스별로 명확해짐
  • HSBC처럼 실제로는 못 가져오는데 duplicates만 쌓이는 착시 제거
  • 다음 실행 자동 재시도 가능
  • 비용
  • collector 반환 계약이 커짐
  • 하우스별 능력 표를 계속 갱신해야 함
  • 되돌리기 비용
  • 낮음. 기존 수집 경로를 없애지 않고 경고·큐 레이어를 덧붙이는 구조이기 때문

4. 결함 B 설계안 — 발견 목록 보존의 표준화

4-1. 설계 목표

  1. 8개 하우스 모두 같은 최소 단위로 “발견 목록 전체”를 남김
  2. 저장 여부뿐 아니라 왜 저장되지 않았는지 남김
  3. 나중에 날짜 하나를 집어도 “발견 → 분류 → 저장/실패”를 재구성 가능하게 함
  4. archive_index.json과 역할을 분리

4-2. 제안 파일 배치

실행별 상세 목록

  • runs/YYYYMMDD/<house>/discovery.jsonl
  • runs/YYYYMMDD/<house>/discovery-summary.json

기존 파일과의 관계

  • 현재 portal-latest.json은 당분간 유지
  • 현재 listing-*.json도 당분간 유지 가능
  • 하지만 새 기준은 discovery.jsonl + discovery-summary.json
  • 대시보드·후속 스크립트는 점진적으로 새 형식으로 이동

4-3. JSONL 한 줄의 최소 스키마

{
  "run_id": "20260812T0545",
  "source": "goldman-sachs",
  "listing_channel": "research_reports",
  "source_doc_id": "9af2c021-...",
  "title": "US IG Weekly",
  "publication_date": "2026-08-12",
  "discovered_rank": 4,
  "routing_action": "include",
  "routing_bucket": "ig_market",
  "routing_reason": "direct_us_ig",
  "fetch_status": "duplicate",
  "archive_key": "goldman-sachs:9af2c021-...",
  "failure_reason": null
}

필수 필드

  • source_doc_id
  • title
  • publication_date
  • routing_action (include | exclude)
  • routing_reason
  • fetch_status
  • saved
  • duplicate
  • excluded_by_routing
  • failed
  • not_attempted_due_cap
  • unsupported_backfill
  • blocked
  • failure_reason

권장 추가 필드

  • listing_channel
  • section
  • page_number 또는 cursor
  • source_url 또는 URL 비식별 참조
  • archive_key
  • raw_meta_path

4-4. 저장 규칙

  • collector가 문서를 “보는 순간” 한 줄 생성
  • 라우팅에서 제외돼도 남김
  • 중복으로 건너뛰어도 남김
  • fetch 실패면 failure_reason 기록
  • 상한 때문에 밀렸으면 not_attempted_due_cap

이렇게 해야 “몇 건을 못 가져왔는지”와 “일부러 안 가져온 것인지”가 갈림.

4-5. archive_index.json과의 관계

archive_index.json

  • 역할: 실제 저장된 원문 파일의 전역 재고표
  • 포함 대상: saved, duplicate로 이어질 수 있는 저장 성공 이력
  • 포함하지 않을 대상: exclude, failed, not_attempted_due_cap

discovery.jsonl

  • 역할: 그날 포털에서 무엇을 봤는지에 대한 증거
  • 포함 대상: 본 모든 것
  • archive_index와의 연결: archive_key로만 참조

즉, archive_index는 창고 목록이고, discovery.jsonl은 현장 출입기록임. 섞으면 둘 다 망가짐.

4-6. 보존기간 정책

현재 runs/가 이미 크므로 목록 보존은 압축 전제로 설계하는 것이 맞음.

권장 정책

  • 최근 14일: 평문 discovery.jsonl
  • 15일~180일: discovery.jsonl.gz
  • 실패·차단·백필대기·unsupported 항목이 있는 날짜: 365일 보존
  • discovery-summary.json: 365일 이상 유지 가능
  • 기존 대형 listing-*.json은 새 구조 안정화 후 요약본만 남기고 압축 또는 제거

이 정책을 권하는 이유

  • 노무라 목록은 이미 MB 단위
  • 하지만 텍스트 반복이 많아 압축 효율이 높음
  • “문제가 있었던 날”은 오래 남겨야 하고, 정상일은 압축만 해도 충분함

4-7. 이 설계의 위험과 트레이드오프

  • 장점
  • 사후 감사가 가능해짐
  • 누락, 의도적 제외, 중복을 명확히 분리 가능
  • 결함 A의 자동 백필 큐가 정확해짐
  • 비용
  • 모든 collector에 공통 기록 로직 추가 필요
  • runs/ 저장량이 증가
  • 되돌리기 비용
  • 낮음. 기존 portal-latest.json을 유지한 채 병행 저장 가능

5. 결함 C 설계안 — 9227 단일 장애점 제거와 안전 재시작

5-1. 최종 권고 구조

권고 결론

종착점은 하우스별 전용 프로필 + 전용 포트가 맞음.

다만 한 번에 5개를 다 찢지 말고 단계적으로 이동해야 함.

이유

  • 지금 문제의 핵심은 “로그인을 다시 해야 해서 불편함”보다 하나 멎으면 5개가 같이 죽는 구조임
  • 세션은 프로필 안 쿠키로 유지될 가능성이 높고, 재시작 설계로 검증 가능함
  • 현재 citi-velocity가 3.0GB까지 비대해졌고, 그 안에 캐시·확장·서비스워커가 누적돼 있음
  • 공유 유지 + 복구 강화만으로는 장애 반경 자체는 줄지 않음

5-2. 왜 다시 왕복하지 않게 되는가

앞으로의 판단 기준을 숫자로 고정해야 함.

공유 유지 허용 조건

아래를 20 영업일 연속 만족할 때만 공유 유지 허용

  1. 같은 포트에 묶인 하우스 수 2개 이하
  2. 프로필 크기 1GB 이하
  3. 웹소켓 stall 0회
  4. blocked.json 유입 0회
  5. 수집 성공률 95% 이상

전용 분리 유지 조건

아래 중 하나라도 만족하면 재통합 금지

  1. 한 포트 장애가 3개 이상 하우스에 영향
  2. 프로필 1GB 초과 또는 증가속도 급격
  3. 같은 포트 stall 1회 이상
  4. 인증 오류가 타 하우스로 전염

이 기준을 문서로 고정하면 “이번엔 통합, 다음엔 재분리”식 왕복을 줄일 수 있음.

5-3. 제안 토폴로지

즉시 목표

  • BofA: 현행 전용 유지
  • Citi: 전용 유지 또는 9227 단독화
  • Morgan Stanley: 전용 분리
  • Goldman Sachs: 전용 분리
  • TD Securities: 전용 분리
  • Barclays: 전용 분리

즉, 9227에 5개를 묶는 구조는 끝내는 방향으로 설계함.

단계적 이행

  1. 1단계: 9227은 Citi만 남기기
  2. 2단계: Morgan Stanley 분리
  3. 3단계: Goldman Sachs 분리
  4. 4단계: TD / Barclays 분리

이 순서를 권하는 이유

  • Morgan과 Goldman은 업무 중요도와 장애 흔적이 큼
  • TD / Barclays는 구조가 비교적 단순함
  • Citi는 기존 프로필의 중심이므로 마지막에 정리하는 편이 안전함

참고

  • 파일시스템에는 matrix-ms, goldman-marquee 프로필 디렉토리가 이미 있으나, 현재 로그인 유효성은 확인 못 함
  • 따라서 “새 프로필 생성”보다 “기존 후보 프로필 재사용 또는 현재 프로필 복제 후 정리”를 비교해야 함

5-4. 프로필 비대화 대응 정책

신규 정책 파일 제안: data/profile_hygiene_policy.json

운영 기준

  • 500MB 초과: 경고
  • 1GB 초과: 정리 예약
  • 2GB 초과: 장애 고위험

정리 대상

  • Default/Cache
  • Default/Code Cache
  • Default/GPUCache
  • extensions_crx_cache
  • 중복 버전 확장 폴더
  • 오래된 Service Worker 캐시

건드리지 말아야 할 것

  • Cookies
  • Login Data
  • Local Storage
  • Preferences 전체 삭제
  • 프로필 전체 초기화

확장 프로그램 기준

  • 브로커 접근에 실제 필요한 확장만 남김
  • 같은 확장의 중복 버전은 최신만 남기고 이전 버전 제거 후보로 표시
  • 확장 제거는 전용 프로필 복제본에서 먼저 검증 후 본 프로필 반영

5-5. “응답은 하지만 조종은 안 되는” 상태의 감지와 재시작 절차

상태 판단 기준

신규 상태 파일: data/browser_health_state.json

포트별로 아래를 기록

  • http_alive
  • ws_alive
  • ws_handshake_ms
  • tab_count
  • profile_size_mb
  • chrome_uptime
  • last_restart_at
  • restart_count_today

자동 재시작 트리거

아래 중 하나면 재시작 후보

  1. /json/version은 응답하지만 ws_alive=false
  2. 웹소켓 handshake 지연이 임계치 초과
  3. collector가 같은 포트에서 연속 connect_over_cdp 실패
  4. warm 단계와 collect 단계 둘 다 같은 stall 확인

안전 재시작 절차

신규 스크립트 제안: scripts/recover_broker_port.py

절차

  1. 포트 잠금 획득
  2. 현재 탭 목록을 runs/YYYYMMDD/browser/port-<port>-pre-restart.json에 저장
  3. 수집 중 아님 확인
  4. Chrome 정상 종료 시도
  5. 제한시간 내 미종료 시 강제 종료
  6. 같은 프로필·같은 포트로 재기동
  7. /json/version + 웹소켓 handshake 확인
  8. 필요한 브로커 홈 탭만 복원
  9. 로그인 여부는 warm_broker_sessions.py의 attention_hint로만 판정
  10. 결과를 runs/YYYYMMDD/browser/port-<port>-restart.json에 기록

중요

  • 재시작은 허용하되 자동 로그인은 하지 않음
  • 세션 유지 여부는 쿠키 기반으로 검증만 함

5-6. 세션 유지 검증 설계

재시작 설계는 “될 것 같다”가 아니라 하우스별 오프아워 리허설로 검증해야 함.

하우스별 검증 항목

  1. 재시작 전 탭 URL, 제목, 인증 확인 힌트 기록
  2. 재시작 후 같은 프로필 재오픈
  3. collector가 로그인 페이지로 가지 않는지 확인
  4. 1건 이상 목록 조회 가능한지 확인
  5. blocked.json 변동 없는지 확인

성공 기준

  • 재시작 후 로그인 재입력 없이 collector 정상 시작
  • warm 상태 attention=null
  • 해당 포트의 ws_alive=true

5-7. 하나 실패해도 나머지가 살아남는 격리 방안

최종 방안

  • 하우스별 전용 포트/프로필
  • 포트별 잠금과 재시작 분리
  • 포트별 건강상태 개별 기록

과도기 방안

  • 전면 분리 전이라도 재시작 단위를 포트 단위로 고정
  • 9227 공유가 남아 있는 동안에는
  • 그 포트 장애를 “하우스 5개 개별 실패”가 아니라 포트 1개 장애로 먼저 인식
  • 다른 전용 포트(BofA 등)는 건드리지 않음

5-8. 이 설계의 위험과 트레이드오프

하우스별 전용 프로필·포트

  • 장점: 장애 반경 최소, 재시작 판단 단순, 프로필 비대화 원인 분리 쉬움
  • 단점: 초기 로그인 작업 증가, 운영 대상 프로필 수 증가, 메모리 사용량 증가 가능
  • 되돌리기 비용: 중간. 다시 통합하려면 포트/프로필 정책을 되돌려야 함

공유 유지 + 복구 강화만 수행

  • 장점: 초반 변경량 적음
  • 단점: 단일 장애점 유지, 재시작해도 5개 동시 영향
  • 되돌리기 비용: 낮음
  • 판단: 과도기 보완책으로만 적합, 종착점으로는 부적합

6. 구현 순서와 의존관계

1단계 — C의 탐지·재시작 가드 먼저

대상

  • warm_broker_sessions.py 확장 또는 별도 복구 스크립트
  • browser_health_state.json

왜 먼저인가

  • 9227 문제는 하루 수집 전체를 한 번에 날릴 수 있음
  • 백필 구조보다 당장 내일 새벽 장애 반경 축소가 더 급함

2단계 — A/B 공통 기반: 능력 레지스트리 + discovery 공통 스키마

대상

  • data/source_capabilities.json
  • discovery.jsonl 스키마

왜 이 순서인가

  • 자동 백필도, 누락 판정도, 목록 보존도 이 두 개를 공통 전제로 씀

3단계 — B 적용: 6개 요약형 collector를 전체 목록 저장형으로 통일

대상

  • BofA, Citi, Morgan Stanley, Goldman, TD, Barclays

왜 이 순서인가

  • 지금 가장 큰 운영 공백은 “놓친 건수 자체를 모른다”는 점임
  • A의 자동 재시도도 B가 있어야 정확해짐

4단계 — A 적용: 자동 백필 큐와 초과요청 경고

대상

  • collect_daily_archive.py
  • backfill_queue.json

왜 이 순서인가

  • 목록 증거가 쌓인 뒤에야 자동 백필 우선순위가 맞아짐

5단계 — C 구조 분리: 9227 해체

대상

  • broker_session_manager.py의 포트/프로필 배치
  • 프로필 정리 정책

왜 마지막인가

  • 구조 분리는 효과가 크지만 리허설 없이 바로 가면 운영 충격이 큼
  • 앞 단계에서 재시작 검증과 상태 기록을 먼저 갖춘 뒤 나가는 편이 안전함

7. 검증 방법

7-1. 결함 A 검증

능력 레지스트리 검증

  • 하우스별 샘플 요청 3종
  • 지원 범위 안
  • 경계값
  • 지원 범위 밖
  • 기대 결과
  • 지원 안: unsupported_backfill 또는 blocked_capability_gap
  • 부분 지원: partial + 정확한 effective_window
  • 최근 재수집으로 조용히 빠지지 않을 것

HSBC 검증

  • rows=100/150/300/500 순차 검증
  • 기록할 것
  • 응답 코드
  • 응답 시간
  • 문서 수
  • 가장 오래된 발행일
  • 중복률
  • 성공 기준
  • source_capabilities.json 값이 실제 결과와 일치

자동 재시도 검증

  • 테스트용 queue 항목 3개 삽입
  • 지원 가능
  • 부분 지원
  • 지원 불가
  • 다음 실행에서 상태 전이가 설계대로 바뀌는지 확인

7-2. 결함 B 검증

목록-요약 정합성 검증

하우스별로 아래가 맞아야 함.

  • discovery.jsonl 전체 줄 수 = discovered
  • routing_action=include 수 = eligible
  • fetch_status=saved 수 = saved
  • fetch_status=duplicate 수 = duplicates
  • fetch_status=failed 수 = failed 개수

사후 재구성 검증

임의의 날짜 1개를 골라 아래 질문이 전부 답돼야 함.

  1. 무엇을 발견했는가
  2. 무엇을 저장했는가
  3. 무엇을 라우팅에서 제외했는가
  4. 무엇이 실패했는가
  5. 무엇이 상한 때문에 시도조차 안 됐는가

archive_index 연결 검증

  • saved 또는 duplicate 레코드만 archive_key를 가져야 함
  • exclude / failed는 archive_key가 없어야 함

7-3. 결함 C 검증

stall 감지 검증

  • warm 단계에서 ws_alive 측정값이 파일로 남는지 확인
  • collector connect_over_cdp 실패와 같은 포트 건강상태가 연결되는지 확인

재시작 검증

하우스별 오프아워 리허설 1회씩

  1. 재시작 전 건강상태 저장
  2. 정상 종료 후 재기동
  3. collector로 목록 1회 확인
  4. 로그인 필요 여부 기록

성공 기준

  • 재기동 후 ws_alive=true
  • 해당 collector가 로그인 페이지가 아니라 실제 목록/본문으로 진입
  • blocked.json 변화 없음

분리 검증

  • 하우스 하나를 전용 포트로 옮긴 뒤
  • 그 포트를 강제 종료해도 다른 하우스 수집이 계속 되는지 확인
  • 성공 기준
  • 장애 반경이 옮긴 하우스 1곳으로 한정

8. 하지 말아야 할 것

  1. --lookback-days만 공통으로 받게 하고, 지원 안 되는 하우스가 최근 것만 다시 가져오게 두기
  2. 이유: 이번 문제의 핵심 착시를 그대로 재생산함

  3. 발견 목록 전체를 archive_index.json에 합치기

  4. 이유: 저장 재고표와 발견 증거가 섞여 중복판정이 오염됨

  5. 골드만 상한 완화로 백필 부족분을 메우기

  6. 이유: 차단 리스크가 설계 제약보다 우선함

  7. blocked.json을 우회하거나 자동 해제하기

  8. 이유: 차단 사고 재발 가능성이 큼

  9. BofA·모간스탠리 조사 전까지 “백필 지원”으로 분류하기

  10. 이유: 확인 못 한 것을 가능하다고 표기하면 다시 같은 오판이 남

  11. 9227을 유지한 채 재시작만 붙이고 끝내기

  12. 이유: 장애 감지는 좋아져도 장애 반경은 그대로임

  13. 5개 하우스를 한 번에 전부 새 프로필로 옮기기

  14. 이유: 세션 유지 검증 없이 한꺼번에 가면 운영 충격이 큼

  15. 프로필 정리 때 쿠키·로그인데이터까지 밀기

  16. 이유: 자동 로그인 확대 금지 조건과 정면 충돌함

9. 승인 판단용 한 줄 결론

  • A: 하우스별 백필 능력을 “가능/부분가능/불가”로 기계적으로 선언하고, 능력 밖 요청은 경고로 막아야 함
  • B: 8개 하우스 모두 발견 목록 전체를 같은 형식으로 남겨야 함
  • C: 9227 공유는 과도기 보완만 하고, 종착점은 하우스별 전용 프로필·전용 포트로 가야 함