- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동 - book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서) - 빈 폴더 제거 (journey/assets/*)
2.5 KiB
2.5 KiB
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 메커니즘 충돌
발생 조건
- uvloop 활성화 상태에서 gRPC 비동기 호출
- 동시 다발적 LLM 요청 (감정 분석 + 요약 생성)
- gRPC PollerCompletionQueue의 이벤트 처리 중 일시적 리소스 부족
해결 방안
1. gRPC 이벤트 루프 로깅 억제 (권장)
위치: rb8001/main.py:1-10
현재:
import uvloop
uvloop.install()
변경:
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]은 일시적 리소스 부족으로 재시도 가능
- 교훈: 오류 코드와 영향 범위 구분 필요