배경
이슈 #503 ~ #507 작업 흐름에서 확인된 현 운영의 priority 분포:
| 토픽 |
트래픽 |
crawl.high |
0건 (시드 0건 + chained/upgrade 모두 명시 안 함) |
crawl.normal |
95%+ — Seed Normal 시드 + Chained 모든 article + Upgrade 하드코딩 |
crawl.low |
거의 0 (google_cse 1건만, interval 6h) |
원인 (publisher level):
bus.SourcePriorityResolver / RuleBasedPriorityResolver 가 wiring 만 됐고 룰 등록 0건 (dead wiring)
chain.go:buildJobMessages 가 job.Priority 미설정 → DefaultPriorityResolver → Normal
upgrader.go:223 가 priority 하드코딩 → Normal
원인 (stage level):
- parser / validate / enrich 가 단일 입력 토픽 (
TopicFetched / TopicNormalized / TopicValidated) 소비
- 단일 토픽 = Kafka partition 순서 FIFO → 같은 stage 안에서 priority sub-ordering 부재
- Kafka header
priority 는 보존되지만 라우팅·정렬에 미사용
→ 결과: priority 분기 메커니즘이 이론적으로 존재 (ExplicitPriorityResolver + 3 토픽 × 3 pool 격리) 이지만 실제 운영에서 차등 적용 안 됨.
변경 방향
Two-track 접근:
- Publisher 단계 — 다음 URL 을 publish 할 때 host/path 기반으로 priority 자동 결정
- Stage 단계 — parser/validate/enrich 가 priority 를 인식해 차등 처리
Track 1 — Publisher 단계 (host/path → priority 매핑)
1-A. Priority 룰 저장 — 옵션 결정 필요
옵션 A (권장): 기존 parser_rules 에 컬럼 추가
ALTER TABLE parser_rules ADD COLUMN crawl_priority SMALLINT NOT NULL DEFAULT 2;
-- 1=high / 2=normal / 3=low — scheduler_entries.priority 와 동일 매핑
- 장점: host_pattern + path_pattern 이 이미 있음 — RuleBasedPriorityResolver 와 자연 1:1
- 단점: parser_rules 의 의미 확장 (selector 외 라우팅 정책 포함)
옵션 B: 신규 crawl_priority_rules 테이블
CREATE TABLE crawl_priority_rules (
id SERIAL PRIMARY KEY,
host_pattern TEXT NOT NULL,
path_pattern TEXT NOT NULL DEFAULT '',
priority SMALLINT NOT NULL CHECK (priority BETWEEN 1 AND 3),
enabled BOOLEAN NOT NULL DEFAULT TRUE,
notes TEXT,
...
);
- 장점: 책임 분리 (parser_rules 는 selector 만, 본 테이블은 라우팅만)
- 단점: 룰 lookup 이중화
옵션 C: 정적 config (fetcher_rules.yaml) 또는 env
- 단점: 운영 중 변경 어려움 — 본 이슈의 목적 (동적 차등) 약화
1-B. RuleBasedPriorityResolver 룰 등록
internal/bus/resolver.go:140+ — AddRule(rule) 메소드 사용해 DB lookup 결과를 wiring 시점에 또는 runtime cache 로 등록.
ruleResolver := bus.NewRuleBasedPriorityResolver(core.PriorityNormal)
for _, r := range priorityRules {
ruleResolver.AddRule(bus.PriorityRule{
Match: func(job *core.CrawlJob) bool {
// host_pattern + path_pattern regex 매칭
return matchHostPath(r.HostPattern, r.PathPattern, job.Target.URL)
},
Priority: priorityFromInt(r.Priority),
})
}
resolver.Add(ruleResolver)
1-C. Chained job 의 priority 명시
internal/bus/chain.go:96+ — buildJobMessages 가 현재 job.Priority 미설정. 옵션:
- (a) 매 URL 마다 resolver chain 통과 (현재 buildMessage 가 이미 함) — 별도 변경 불필요. RuleBased 룰 등록만 하면 자동 작동
- 즉 본 sub-task 는 1-B 가 wiring 되면 자연 해결
1-D. Upgrade 의 priority 하드코딩 제거
internal/processor/fetcher/rule/upgrader.go:223,235 — Priority: core.PriorityNormal + Topic: queue.TopicCrawlNormal 하드코딩.
- 변경: buildMessage 경유하도록 PublishUpgrade 리팩터 → resolver chain 통과
- 또는 원 raw_id 의 host 기반 lookup
Track 2 — Stage 단계 priority-aware 처리
parser / validate / enrich 가 현재 단일 토픽 소비. priority 차등 처리 옵션:
옵션 2-A (권장): 토픽 3분 — fetcher 패턴 그대로
각 stage 도 <stage>.high / <stage>.normal / <stage>.low 3개 토픽 + 3개 worker pool 분리.
| Stage |
현재 |
신규 |
| Parser |
issuetracker.fetched × 1 |
issuetracker.fetched.{high,normal,low} × 3 |
| Validate |
issuetracker.normalized × 1 |
issuetracker.normalized.{high,normal,low} × 3 |
| Enrich |
issuetracker.validated × 1 |
issuetracker.validated.{high,normal,low} × 3 |
- 장점: fetcher 와 동일 패턴, resource isolation 보장 (High 항상 즉시 처리)
- 단점:
- 토픽 수 증가 (4 → 12 토픽)
- 환경변수
*_HIGH_WORKER_COUNT / NORMAL / LOW 신설
- 메시지 발행 시점에 priority 결정 (RawContentRef / ContentRef Kafka 헤더 사용)
- migration cost — 기존 consumer/producer wiring 전반 수정
옵션 2-B: In-memory priority queue per worker
Worker pool 이 단일 토픽 consume 후 in-process priority queue (heap) 에 적재, worker 가 priority 순으로 pop.
- 장점: 토픽 변경 없음 — 기존 인프라 유지
- 단점:
- 인스턴스별 격리 (다른 인스턴스의 High 를 못 봄)
- graceful shutdown 시 in-memory 잔존물 손실
- 단일 인스턴스에서만 효과적
옵션 2-C: Redis ZSET intermediate queue
Stage 별로 Kafka → Redis ZSET (score=priority+timestamp) → worker pop.
권장 phasing
본 이슈는 substantial — sub-issue 로 분리 권장:
- Sub 1 [FEAT]: Priority 룰 저장 스키마 (옵션 A or B 결정)
- Sub 2 [FEAT]: RuleBasedPriorityResolver DB-backed 룰 lookup + cache + wiring
- Sub 3 [FIX]: Upgrade 하드코딩 priority 제거 + buildMessage 경유
- Sub 4 [FEAT]: Parser stage priority-aware (옵션 2-A or 2-C 결정)
- Sub 5 [FEAT]: Validate stage priority-aware
- Sub 6 [FEAT]: Enrich stage priority-aware
Sub 13 만 먼저 진행하면 Publisher 단계의 priority 차등은 즉시 효과 — Stage 단계 (Sub 46) 는 별도 phase.
완료 조건
Why
- 현 운영에서 95%+ 트래픽이 Normal 토픽 단일 경로 → priority 분기 인프라가 사실상 미사용
- breaking-news / urgent-update / archive 등 호스트/path 별 처리 우선순위 차등이 시스템 자원 활용 효율 향상
- ExplicitPriorityResolver 만 동작 중 — 다른 두 resolver 는 dead wiring, 본 이슈가 활성화
우선순위
Medium — 현재 시스템이 priority 분기 없이도 정상 동작 중 (resource isolation 으로 High 가 굶지 않음). 그러나 미래 확장 (breaking-news 소스 추가 등) 시 본 차등이 핵심.
관련
배경
이슈 #503 ~ #507 작업 흐름에서 확인된 현 운영의 priority 분포:
crawl.highcrawl.normalcrawl.low원인 (publisher level):
bus.SourcePriorityResolver/RuleBasedPriorityResolver가 wiring 만 됐고 룰 등록 0건 (dead wiring)chain.go:buildJobMessages가job.Priority미설정 → DefaultPriorityResolver → Normalupgrader.go:223가 priority 하드코딩 → Normal원인 (stage level):
TopicFetched/TopicNormalized/TopicValidated) 소비priority는 보존되지만 라우팅·정렬에 미사용→ 결과: priority 분기 메커니즘이 이론적으로 존재 (ExplicitPriorityResolver + 3 토픽 × 3 pool 격리) 이지만 실제 운영에서 차등 적용 안 됨.
변경 방향
Two-track 접근:
Track 1 — Publisher 단계 (
host/path → priority매핑)1-A. Priority 룰 저장 — 옵션 결정 필요
옵션 A (권장): 기존
parser_rules에 컬럼 추가옵션 B: 신규
crawl_priority_rules테이블옵션 C: 정적 config (
fetcher_rules.yaml) 또는 env1-B. RuleBasedPriorityResolver 룰 등록
internal/bus/resolver.go:140+—AddRule(rule)메소드 사용해 DB lookup 결과를 wiring 시점에 또는 runtime cache 로 등록.1-C. Chained job 의 priority 명시
internal/bus/chain.go:96+—buildJobMessages가 현재job.Priority미설정. 옵션:1-D. Upgrade 의 priority 하드코딩 제거
internal/processor/fetcher/rule/upgrader.go:223,235—Priority: core.PriorityNormal+Topic: queue.TopicCrawlNormal하드코딩.Track 2 — Stage 단계 priority-aware 처리
parser / validate / enrich 가 현재 단일 토픽 소비. priority 차등 처리 옵션:
옵션 2-A (권장): 토픽 3분 — fetcher 패턴 그대로
각 stage 도
<stage>.high / <stage>.normal / <stage>.low3개 토픽 + 3개 worker pool 분리.issuetracker.fetched× 1issuetracker.fetched.{high,normal,low}× 3issuetracker.normalized× 1issuetracker.normalized.{high,normal,low}× 3issuetracker.validated× 1issuetracker.validated.{high,normal,low}× 3*_HIGH_WORKER_COUNT / NORMAL / LOW신설옵션 2-B: In-memory priority queue per worker
Worker pool 이 단일 토픽 consume 후 in-process priority queue (heap) 에 적재, worker 가 priority 순으로 pop.
옵션 2-C: Redis ZSET intermediate queue
Stage 별로 Kafka → Redis ZSET (score=priority+timestamp) → worker pop.
권장 phasing
본 이슈는 substantial — sub-issue 로 분리 권장:
Sub 1
3 만 먼저 진행하면 Publisher 단계의 priority 차등은 즉시 효과 — Stage 단계 (Sub 46) 는 별도 phase.완료 조건
RuleBasedPriorityResolverDB lookup + main.go wiringPublishUpgrade하드코딩 제거 — buildMessage 경유.env.example+ 운영 문서 갱신 (priority 룰 설정 가이드)Why
우선순위
Medium — 현재 시스템이 priority 분기 없이도 정상 동작 중 (resource isolation 으로 High 가 굶지 않음). 그러나 미래 확장 (breaking-news 소스 추가 등) 시 본 차등이 핵심.
관련
bus.PriorityResolverchain —internal/bus/resolver.gointernal/bus/chain.go:96+internal/processor/fetcher/rule/upgrader.go:223,235