[FIX#347] fetcher registry — BaseURL strict check 제거 + 호스트별 base_url 허용 - #350
Conversation
… (이슈 #347) 배경: - 라이브 boot fail: 'inconsistent source metadata for source_name=\"dcinside\" (host=\"gall.dcinside.com\" vs host=\"gallery.dcinside.com\")' - dcinside / reddit / slashdot 의 host 별 다른 base_url (PR #334/#335 의도) 이 strict check 에 거부됨 심층 분석 결과: - BaseURL 의 사용처는 GoQuery/Generic HealthCheck (3 line) + raw_contents persistence 만 - rate_limiter 의 RPH lookup 은 SourceConfigResolver.Resolve(host_pattern) 으로 host 단위 — source_name 단일 BaseURL 가정과 무관 - bySource collapse 는 canonical (base_url hostname == host_pattern) 하나만 사용 — 비-canonical 의 BaseURL 은 이미 무시되고 있었음 - → BaseURL strict check 는 거짓 양성만 야기, 보호 가치 없음 수정: 1. AnalyzeSources 함수 분리 (registry.go 의 collapse 로직 단위 테스트 친화로 추출) - 시그니처: (rules) -> (bySource, hostsBySource, baseURLsBySource, err) - SourceEntry 타입도 함께 export — Rec / HasExact 2. strict check 에서 BaseURL 제외: - 유지: Country / Language / SourceType / RequestsPerHour (의미론적 메타데이터) - 제거: BaseURL (HealthCheck 만 사용, 호스트별 차이 허용) 3. baseURLsBySource 캡쳐 + 등록 로그에 base_urls / canonical_base_url 추가: - 같은 source 의 host 들이 다른 base_url 을 가지면 운영 가시성 위해 명시 - canonical 만 HealthCheck 사용한다는 사실 로그에서 명확화 테스트 9건 (test/internal/processor/fetcher/domain/general/sources/analyze_test.go): - MultiHostDifferentBaseURLs_Pass — 이슈 #347 핵심 회귀 (dcinside dual-host) - CanonicalSelection_PrefersBaseURLHostMatch — canonical 선택 보존 - RPHMismatch / CountryMismatch / LanguageMismatch / SourceTypeMismatch — 각 reject - EmptySourceNameSkipped — legacy row skip - BaseURLsSetCapturesAllVariants — 다양성 캡쳐 - SingleHostUniformBaseURLsSet — 단일 host set size=1
라이브 #11 (PR #346 머지 직후) 의 manual SQL 우회 (운영자가 dcinside/reddit/slashdot 의 base_url 을 source_name 별 단일 값으로 통일) 를 #347 의 strict check 완화 코드 변경 후의 정합 상태로 복원. 변경: - dcinside / gallery.dcinside.com → https://gallery.dcinside.com - reddit / www.reddit.com → https://www.reddit.com - slashdot / news.slashdot.org → https://news.slashdot.org 각 UPDATE 는 host_pattern + source_name 으로 정확히 식별. 멱등 — 이미 값이면 noop. 기능 영향: HealthCheck 가 canonical 만 사용하므로 비-canonical 의 base_url 차이는 runtime 영향 0. DB 의 의도된 host-specific 값 정합 보존 의도.
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the source registration logic by extracting analysis into a new AnalyzeSources function. This change allows sources to have multiple base URLs, resolving Issue #347, while still enforcing consistency for other metadata like Country and Language. It also includes SQL migrations to restore host-specific base URLs and comprehensive tests for the new analysis logic. Review feedback suggests enhancing error messages to pinpoint specific metadata mismatches and unexporting internal types and functions to keep the public API clean.
There was a problem hiding this comment.
Pull request overview
이 PR은 fetcher registry의 source 분석/등록 로직에서 동일 source_name 내 host별 base_url 차이를 허용하도록 strict check를 완화해, dcinside/reddit/slashdot 같은 dual-host seed에서 발생하던 boot fail을 제거합니다. 또한 분석 로직을 함수로 분리해 테스트 가능하게 만들고, 운영 가시성을 위한 로그 및 데이터 정합 복원 마이그레이션을 추가합니다.
Changes:
RegisterAll의 source collapse 로직을AnalyzeSources로 분리하고, strict consistency check에서 BaseURL 비교를 제외- 동일 source_name에 base_url 변형이 여러 개면 이를 운영 로그로 명시
- host별 base_url 정합을 복원하는 migration 025(up/down) 및
AnalyzeSources단위 테스트 추가
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| internal/processor/fetcher/domain/general/sources/registry.go | AnalyzeSources로 분석 로직 분리, BaseURL strict check 제거, multiple base_urls 로그 강화 |
| test/internal/processor/fetcher/domain/general/sources/analyze_test.go | host별 base_url 차이 허용/거부 조건 및 canonical 선택 회귀 테스트 추가 |
| migrations/up/025_fetcher_rules_per_host_base_url.sql | 운영 우회로 통일됐던 base_url을 host별 의도 값으로 복원 |
| migrations/down/025_fetcher_rules_per_host_base_url.sql | 롤백 시 운영 우회 상태(통일 값)로 되돌리는 DOWN 추가 |
…igration gemini 2건 + Copilot 8건 일괄 반영: 1. AnalyzeSources: 어느 필드가 mismatch 인지 명시 (gemini #1) - 기존: 'inconsistent source metadata for source_name=X (host=A vs host=B)' - 변경: 'inconsistent Country for source_name=X ...' / Language / SourceType / RequestsPerHour - 운영 boot fail 시 즉시 진단 가능 2. AnalyzeSources export 사유 doc 추가 (gemini #2 응답) - 프로젝트 규칙 (.claude/rules/05-testing.md) — 모든 테스트는 test/internal 하위 외부 _test - same-package _test.go (internal/<pkg>/*_test.go) 패턴은 디렉토리 컨벤션 위반 - testability 최소 노출로 export — API 안정 약속 아님 명시 3. RegisterAll: AnalyzeSources error wrap (Copilot #1) - 'analyze sources: %w' 로 호출 스택 컨텍스트 보존 4. 등록 로그: distinct base_urls 정렬 (Copilot #2) - map iteration 비결정성 → sort.Strings(distinct) 로 안정화 5. migrations 025 up/down: <> → IS DISTINCT FROM (Copilot #3-#8, 6건) - base_url 컬럼이 NULL 허용 (migration 014) — '<>' 비교는 NULL 시 NULL 반환 → 조건 skip - IS DISTINCT FROM 으로 NULL 포함 처리 — NULL row 도 정확히 갱신 테스트 assertion 업데이트 — 'inconsistent source metadata' → 'inconsistent ' 로 부분 매치.
연관 이슈
Closes #347
배경 — 라이브 #11 boot fail
`make build && ./bin/issuetracker` 라이브 기동 시 fatal:
```
{"level":"fatal","error":"inconsistent source metadata for source_name=\"dcinside\" (host=\"gall.dcinside.com\" vs host=\"gallery.dcinside.com\")",...}
```
dcinside / reddit / slashdot 의 host 별 다른 base_url (PR #334/#335 의도) 이 `RegisterAll` 의 strict check 에 거부됨.
심층 분석 결과 (issue #347 본문에 기록)
BaseURL 의 실제 사용처
rate_limiter 의 RPH lookup
bySourcecollapse 의 실상결론
구현
1.
AnalyzeSources함수 분리 (testability)`RegisterAll` 안에 inline 되어 있던 collapse 로직을 패키지 레벨 함수로 추출:
```go
func AnalyzeSources(rules []*storage.FetcherRuleRecord) (
bySource map[string]SourceEntry,
hostsBySource map[string][]string,
baseURLsBySource map[string]map[string]struct{},
err error,
)
```
`SourceEntry { Rec, HasExact }` 도 export — 단위 테스트 친화.
2. strict check 에서 BaseURL 제외
```go
if prev.Rec.Country != r.Country ||
prev.Rec.Language != r.Language ||
// BaseURL 제거 — HealthCheck 만 사용, 호스트별 차이 허용
prev.Rec.SourceType != r.SourceType ||
prev.Rec.RequestsPerHour != r.RequestsPerHour {
return ..., fmt.Errorf("inconsistent source metadata ...")
}
```
3. 등록 로그 강화
같은 source_name 의 host 들이 다른 base_url 을 가지면 운영 가시성 위해 명시:
```
crawler registered from db (multiple base_urls — canonical used for HealthCheck only)
source=dcinside hosts=[gall.dcinside.com gallery.dcinside.com]
base_urls=[https://gall.dcinside.com https://gallery.dcinside.com]
canonical_base_url=https://gall.dcinside.com
```
4. Migration 025 — 라이브 우회 정리
라이브 #11 의 manual SQL (운영자가 base_url 통일) 을 host-specific 값으로 정합 복원:
기능 영향 0 (HealthCheck canonical 만 사용), DB 의 의도된 host-specific 값 정합 보존.
테스트 (9건)
`test/internal/processor/fetcher/domain/general/sources/analyze_test.go`:
CI / 머지 게이트 점검
변경 영향 범위 + 위험도
롤백 계획
🤖 Generated with Claude Code