본문 바로가기
Python

SQLAlchemy - QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00

by 올엠 2024. 3. 25.
반응형

 

 

최근 작업요청이 많아지는 것 같더니, 다량의 호출로 인해 Database 접근 오류가 발생하였다.

오류내용은 다음과 같다.

QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30.00 (Background on this error at: https://sqlalche.me/e/14/3o7r)
 

Error Messages — SQLAlchemy 1.4 Documentation

Previous: Third Party Integration Issues Next: Changes and Migration Up: Home On this page: Error Messages Connections and Transactions DBAPI Errors SQL Expression Language Object Relational Mapping AsyncIO Exceptions Core Exception Classes ORM Exception C

docs.sqlalchemy.org

원인을 확인해보면, 실제 Database 자체에 부하가 아닌 Database와 연결을 해주는 Connection 부분이 Overflow 오류이다.

즉 메세지 내용대로 Database에 접근할 수 있는 연결 가능 수가 5 개이고 대기 가능수가 10개인데 이 수치를 넘어가면서 더이상 연결을 할 수 없는 문제가 발생한 것이다.

대부분 Database하고 Connection은 특정한 갯수로 연결하여 관리하는데, 과도한 요청이 발생하면 이와같이 연결 오류가 발생할 수 있다.

이 오류를 해결하기 위해서는 2가지를 진행하는 것을 추천한다.

1. Database Connection Size 올리기

가장 기본적으로 해야 하는 부분으로 Database의 연결할 수 있는 Connection 수를 기존 보다 늘리는 방향이다.

engine = create_engine("mysql://u:p@host/db", pool_size=10, max_overflow=20)

필자의 Database init 함수 부분에 추가한다면 아래와 같이 추가 하여 기본 연결 수와 최대 대기수를 늘릴 수 있다.

    def init_app(self, databasename, app: FastAPI, **kwargs):
        """
        DB 초기화 함수
        """
        self._engine = create_engine(
            database,
            echo=db_echo,
            pool_recycle=900,
            pool_pre_ping=True,
            pool_size=10,
            max_overflow=20
        )

        self._session = sessionmaker(autocommit=False, autoflush=False, bind=self._engine)

 

아래 문서에서도 보면, 보다 자세한 설정 방법을 알 수 있다.

Error Messages — SQLAlchemy 1.4 Documentation

2. 연결 시도 자체에 대한 지연 연결 구성

추가로 Database의 연결할 수 있는 Connection 수로 문제를 해결 할 수 있지만, 과도한 Connection 설정으로 실제 Database에 많은 연결이 있을 경우 Database가 처리하지 못하는 경우까지 갈 수 있다. 따라서 연결 시도 자체에 지연을 줄 수 있도록 구성하면 효과적이다.

Python 코드로 지연 코드를 작성해 본다면, 위 Connection 설정을 적절하게 변경한후 오류가 발생하였을 때, 대기 시간을 주는 방식이다.

아래는 필자의 FastAPI의 db session을 설정하는 부분이다.

    def get_db(self):
        """
        요청마다 DB 세션 유지 함수
        :return:
        """
        if self._session is None:
            raise Exception("must be called 'init_app'")
        db_session = None
        is_error = True # 데이터 베이스에서 오류가 있는 경우 재시도 할 수 있도록 함
        retry_count = 0
        while is_error:
            try:
                db_session = self._session()
                is_error = False
                yield db_session
            except Exception:
                if retry_count > 5:
                    is_error = False # 무한 시도 방지
                time.sleep(1.5)
                retry_count += 1
            finally:
                db_session.close()

 

이외에도 호출 부분에서도 적절한 유휴타임을 준다면 보다 효과적으로 Database 접근 부하를 줄일 수 있을 것이다.

반응형