DOCS/journey/troubleshooting/251016_grpc_uvloop_blocking_error.md
Claude-51124 22557e7132 docs: 오래된 트러블슈팅 아카이브 및 구조 정리
- 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/*)
2025-11-17 14:06:05 +09:00

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 메커니즘 충돌

발생 조건

  1. uvloop 활성화 상태에서 gRPC 비동기 호출
  2. 동시 다발적 LLM 요청 (감정 분석 + 요약 생성)
  3. 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]은 일시적 리소스 부족으로 재시도 가능
  • 교훈: 오류 코드와 영향 범위 구분 필요