# M1 — ProcessingQueue Throughput Baseline

**측정일**: 2026-05-02
**대상**: GPU 서버 `hyungi_document_server-postgres-1` (DB pkm)
**범위**: read-only SELECT 만. 4.0 가드 준수 (docker-compose / migration / queue stage / worker restart / source_channel='web' insert / SearXNG·Crawl4AI 도입 모두 0건).
**plan 참조**: `~/.claude/plans/users-hyungiahn-library-cloudstorage-sy-binary-dongarra.md` §4 (LocalResearch Feasibility).

---

## 1. Stage별 7일 throughput

```sql
SELECT stage::text,
  COUNT(*) FILTER (WHERE status='pending') AS pending,
  COUNT(*) FILTER (WHERE status='processing') AS processing,
  COUNT(*) FILTER (WHERE status='completed') AS completed,
  COUNT(*) FILTER (WHERE status='failed') AS failed,
  ROUND(AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed')::numeric, 2) AS avg_sec,
  ROUND((PERCENTILE_CONT(0.5)  WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed'))::numeric, 2) AS p50_sec,
  ROUND((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM (completed_at - started_at))) FILTER (WHERE status='completed'))::numeric, 2) AS p95_sec
FROM processing_queue
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY stage ORDER BY stage;
```

| stage | pending | processing | completed | failed | avg_sec | p50_sec | p95_sec |
|---|---:|---:|---:|---:|---:|---:|---:|
| chunk | 4 | 0 | 453 | 0 | 1.36 | 0.76 | 5.19 |
| classify | 8 | 2 | 642 | 1 | **42.89** | 33.71 | **82.17** |
| deep_summary | 4 | 0 | 467 | 0 | 25.65 | 16.37 | 73.26 |
| embed | 13 | 0 | 2,839 | 0 | 0.26 | 0.10 | 1.75 |
| extract | 2 | 0 | 345 | 0 | 0.02 | 0.01 | 0.08 |
| markdown | 4 | 0 | 89 | 1 | 11.62 | 0.01 | 77.13 |
| preview | 13 | 0 | 332 | 0 | 0.01 | 0.01 | 0.02 |
| summarize | 0 | 0 | 2,395 | 0 | 3.74 | 3.61 | 5.67 |

**핵심 관찰**:
- **classify**: 가장 무거운 stage (avg 42.89초, p95 82초) — MLX gemma 호출 dominant
- **deep_summary / summarize**: 두 번째로 무거움 (LLM 호출)
- **embed**: 가장 가벼움 (avg 0.26초) + 처리량 최대 (2,839건)
- **markdown**: bimodal (p50 0.01초 = skip, p95 77초 = 실제 marker-pdf 변환). 2026-05-01 Phase 1B 배포 첫날 가동.
- **failed**: classify 1건, markdown 1건 (총 2건, 0.06%) — 무시 수준

---

## 2. 일별 처리량 분포

```sql
SELECT date_trunc('day', completed_at)::date AS day, stage::text, COUNT(*)
FROM processing_queue
WHERE completed_at > NOW() - INTERVAL '7 days' AND status='completed'
GROUP BY 1, 2 ORDER BY 1 DESC, 2;
```

| day | classify | deep_summary | embed | summarize | chunk | markdown | extract | preview |
|---|---:|---:|---:|---:|---:|---:|---:|---:|
| 2026-05-02 | 6 | 1 | 66 | 73 | 1 | 1 | 15 | 2 |
| 2026-05-01 | 55 | 55 | 377 | 322 | 55 | **88** | 55 | 55 |
| 2026-04-30 | — | — | 450 | 412 | — | — | — | — |
| 2026-04-29 | — | — | 396 | 434 | — | — | — | — |
| 2026-04-28 | — | 15 | 342 | 342 | — | — | — | — |
| 2026-04-27 | **258** | 257 | 627 | 369 | 258 | — | 273 | 273 |
| 2026-04-26 | — | — | 283 | 283 | — | — | 2 | 2 |
| 2026-04-25 | 139 | 139 | 341 | 186 | 139 | — | — | — |

**해석**:
- 2026-04-25 / 04-27: 가스기사 학습자료 일괄 업로드 batch (PDF/markdown 2,100문항 흐름 흔적, 메모리 `project_gas_engineer_study.md`)
- 2026-05-01: Phase 1B marker_worker 배포 첫날, markdown 88건 첫 가동
- 평일 비-batch 일은 classify 0~6건, embed 280~450건 (study_topics PR-4 자동 임베딩 포함)
- **편차 매우 큼**: classify 0~258/일, embed 66~627/일. 일 평균보다 peak 일 capacity 가 운영 게이트의 의미 있는 기준.

---

## 3. Capacity 분석 (concurrency=1 가정, LLM stage)

LLM stage 는 `feedback_analyzer_async_only.md` + Phase 2 측정 (gemma-4 ~10초, semaphore=1 필수) 기반 **concurrency 1 직렬 처리 가정**. embed 는 GPU batch 가능, chunk/extract/preview 는 CPU light.

| stage | avg_sec | concurrency | max capacity (건/일) | 7일 평균 처리량 (건/일) | utilization |
|---|---:|---:|---:|---:|---:|
| classify | 42.89 | 1 | 2,014 | 91.7 | **4.6%** |
| deep_summary | 25.65 | 1 | 3,368 | 66.7 | 2.0% |
| summarize | 3.74 | 1 | 23,101 | 342.1 | 1.5% |
| markdown (PDF) | 11.62 | 1 | 7,436 | 12.7 | 0.2% |
| embed | 0.26 | (GPU batch) | ≫ 100k | 405.6 | < 0.5% |
| chunk | 1.36 | (CPU) | 63,529 | 64.7 | 0.1% |

**peak 일 (2026-04-27) 부하**:
- classify 258건/일 = 11,065초 = **3.07시간/일** = 12.8% utilization (concurrency 1, peak day)
- deep_summary 257건/일 = 6,592초 = 1.83시간/일 = 7.6% utilization

평일 평균은 여유롭지만 **peak batch 일에는 classify utilization 13% 까지 도달**. web 추가 부하는 평균이 아닌 peak 기준으로 평가해야 함.

---

## 4. Web 30 URL/일 추가 부하 추정

PDF 시나리오: 사용자 검색 1회당 web 30 URL 수집 → 각 URL 이 markdown / classify / embed / chunk / summarize stage 진입 가정.

| stage | 추가 건/일 | 단위 시간 (초) | 추가 시간/일 (초) | peak 일 utilization 증가 |
|---|---:|---:|---:|---:|
| markdown (web HTML) | 30 | ~5 추정¹ | 150 | +0.2% |
| classify | 30 | 42.89 | 1,287 | **+1.5%** |
| deep_summary | 30 | 25.65 | 770 | +0.9% |
| summarize | 30 | 3.74 | 112 | +0.1% |
| embed | 150 (5 chunks/doc) | 0.26 | 39 | +0.05% |
| chunk | 150 | 1.36 | 204 | +0.2% |

¹ web HTML → trafilatura 변환은 marker-pdf (PDF 11.62초) 보다 훨씬 빠름 (보통 0.5~3초). 5초는 보수적 가정.

**총 추가 capacity 사용** (가장 무거운 LLM stage 기준):
- classify utilization 증가: peak 일 12.8% → **14.3%** (+1.5%p)
- 평일 평균 utilization 증가: 4.6% → **6.0%** (+1.4%p)

---

## 5. M1 게이트 판정

**plan §4.1 게이트 1**: web 30 URL/일 추가 부하가 현재 ProcessingQueue 처리 capacity 의 **10% 이하**.

| 측정 | 값 | 임계값 | 판정 |
|---|---|---|---|
| classify capacity 추가 사용량 | +1.5%p (절대) | ≤ 10%p | ✓ PASS |
| 전체 LLM stage 합산 추가 사용량 | +2.5%p (1.5+0.9+0.1) | ≤ 10%p | ✓ PASS |
| peak 일 classify utilization (after) | 14.3% | < 50% (여유) | ✓ PASS |

**M1 게이트 1: PASS** (추가 부하가 capacity 의 10% 보다 충분히 작음 — peak 일 기준 +1.5%p).

---

## 6. 보강 관찰 (참고)

- **Failed 건 2건만 발생** (classify 1, markdown 1) — 운영 안정.
- **Phase 1B markdown stage 가동 검증**: 88 completed / 1 failed / 4 pending — 정상 가동 (메모리 `project_markdown_canonical_layer.md` Phase 1B 배포 일치).
- **embed 의 처리량은 chunks 단위**: 1 doc 당 평균 ~5 chunks 가정 → 150 chunks/일 추가는 GPU bge-m3 capacity 에 비해 매우 작음 (M3 에서 배치 처리량 측정 후 재확인).

## 7. 다음 단계

- M2 (MLX gemma-4 N=1/N=2 capacity 벤치) — 차단 조건 (error_rate>0, p95 3배 증가, healthcheck fail, /api/search/ask 또는 eval runner 시간대 충돌) 사전 점검 후 진행.
