# gRPC + uvloop BlockingIOError 리소스 경합

**날짜**: 2025-10-16
**작성자**: Claude
**관련 파일**: `rb8001/main.py`, `rb8001/app/llm/emotion_classifier.py`

---

## 문제 상황

### 발생 시점
- NaverWorks Daily Briefing 실행 시 (매일 09:00)
- 감정 분석 LLM 호출 중 발생 (09:00:03.240)

### 에러 로그
```
{"time":"2025-10-16 09:00:03,240","level":"ERROR","module":"asyncio"}
Exception in callback functools.partial(<bound method PollerCompletionQueue._handle_events of <grpc._cython.cygrpc.PollerCompletionQueue object>>)
File "grpc/_cython/_cygrpc/aio/completion_queue.pyx.pxi", line 147, in grpc._cython.cygrpc.PollerCompletionQueue._handle_events
BlockingIOError: [Errno 11] Resource temporarily unavailable
```

### 영향
- 작업 실패 없음 (NaverWorks Briefing 정상 완료)
- 로그 노이즈 발생으로 실제 오류 추적 방해

---

## 원인 분석

### 기술적 원인
- **uvloop**: rb8001 main.py에서 사용 중인 고성능 event loop
- **gRPC**: emotion_classifier.py에서 Vertex AI (Gemini) 호출 시 사용
- **리소스 경합**: uvloop의 epoll과 gRPC의 polling 메커니즘 충돌

### 발생 조건
1. uvloop 활성화 상태에서 gRPC 비동기 호출
2. 동시 다발적 LLM 요청 (감정 분석 + 요약 생성)
3. gRPC PollerCompletionQueue의 이벤트 처리 중 일시적 리소스 부족

---

## 해결 방안

### 1. gRPC 이벤트 루프 로깅 억제 (권장)

**위치**: rb8001/main.py:1-10

**현재**:
```python
import uvloop
uvloop.install()
```

**변경**:
```python
import uvloop
import logging
uvloop.install()
logging.getLogger("grpc").setLevel(logging.CRITICAL)
```

### 2. asyncio 기본 루프 사용 (성능 하락)

**위치**: rb8001/main.py:1-10

**변경**: uvloop.install() 제거, asyncio 기본 루프 사용

### 3. gRPC 채널 재사용 설정

**위치**: rb8001/app/llm/emotion_classifier.py

**확인 필요**: gRPC 채널 풀링 설정 확인

---

## 구현 완료

미구현 (로그 노이즈만 발생, 기능 영향 없음)

---

## 교훈

### uvloop + gRPC 조합 주의
- uvloop은 asyncio보다 빠르지만 gRPC와 호환성 이슈 존재
- 교훈: 성능 라이브러리 도입 시 의존성 충돌 사전 검증

### 로그 레벨 관리
- ERROR 레벨 로그가 실제 오류가 아닐 수 있음
- 교훈: 외부 라이브러리 로그는 필요 시 레벨 조정

### 일시적 오류 vs 치명적 오류
- BlockingIOError [Errno 11]은 일시적 리소스 부족으로 재시도 가능
- 교훈: 오류 코드와 영향 범위 구분 필요
