본문 바로가기

FastAPI

Multi-Worker Redis 연결 문제 해결(Claude Agent)

에러: TypeError: 'NoneType' object is not callable
원인: Dockerfile에 설정해 놓은 Gunicorn 9개 worker가 메인 프로세스에서 생성된 Redis 연결을 공유하면서 asyncio 이벤트 루프 충돌 발생

✅ 해결 방법

1. Redis 연결 Lazy Initialization (app/core/redis.py)

아래의 코드를 

redis_pool = ConnectionPool(
    host=host,
    port=port,
    db=db,
    password=password,
    decode_responses=True,  # 문자열 응답을 자동으로 디코딩
    max_connections=10,
    socket_keepalive=True,
    socket_connect_timeout=5,
    socket_timeout=5,
    retry_on_timeout=True,)

redis_client = Redis(connection_pool=redis_pool)

 

아래처럼 수정함

# 각 worker 프로세스마다 독립적인 연결을 생성하기 위해 None으로 초기화
redis_pool = None
redis_client = None

def get_redis_pool():
    """각 worker 프로세스마다 독립적인 connection pool 생성

    gunicorn의 여러 worker 프로세스 간 Redis 연결 공유 문제 해결:
    - asyncio 기반 Redis 클라이언트는 프로세스 간 공유 불가
    - 각 worker가 자체 연결 풀을 생성하도록 lazy initialization 적용
    """
    global redis_pool
    if redis_pool is None:
        redis_pool = ConnectionPool(
            host=host,
            port=port,
            db=db,
            password=password,
            decode_responses=True,
            max_connections=10,
            socket_keepalive=True,
            socket_connect_timeout=5,
            socket_timeout=5,
            retry_on_timeout=True,
        )
    return redis_pool

def get_redis_client():
    """각 worker 프로세스마다 독립적인 Redis client 생성

    사용법:
        redis_client = get_redis_client()
        await redis_client.set("key", "value")
    """
    global redis_client
    if redis_client is None:
        redis_client = Redis(connection_pool=get_redis_pool())
    return redis_client

 

2. 수정된 파일 목록

redis_client가 적용된 관련파일들을 모두 수정함
        ✅ app/core/redis.py - Redis 클라이언트 팩토리 함수 추가
        ✅ app/services/token_service.py - 모든 메서드에 get_redis_client() 적용
        ✅ app/utils/wysiwyg.py - 모든 함수에 get_redis_client() 적용
        ✅ app/core/inits.py - startup/shutdown에 get_redis_client() 적용
        ✅ app/apis/accounts.py - 모든 엔드포인트에 get_redis_client() 적용
        ✅ app/apis/articles/articles.py - import 변경
        ✅ app/apis/articles/comments.py - import 변경

🎯 핵심 변경 패턴

# 각 함수/메서드 시작 부분에 추가
async def some_function():
    redis_client = get_redis_client()  # ← 추가
    await redis_client.set("key", "value")

🚀 결과
✅ 각 worker 프로세스가 독립적인 Redis 연결 생성
✅ Asyncio 이벤트 루프 충돌 해결