# 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(>) 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]은 일시적 리소스 부족으로 재시도 가능 - 교훈: 오류 코드와 영향 범위 구분 필요