1 분 소요

문제 상황

MySQLdb.OperationalError : (2013, ‘Lost connection to MySQL server during query’)

몇초 내로 응답이 나가는 일반적인 요청이 아닌 분 단위로 걸리는 요청을 처리하다보니 세션이 끊기는 현상이 있었다.
trackback에 나오는 애러 발생 로직들은 서버 로직에서 출발한게 아니었고, 에러 메시지를 보니 커넥션 이슈라고 생각이 들었다.
생각해보니 DB 커넥션 설정을 별도로 수정하지 않고 사용해왔고, 오래 걸리는 작업이 최근에 부쩍 늘게 돼서 이제야 마주칠 수 있던
이슈는 아니었을까 생각이 들었다.

해결 방법

pool_pre_ping 설정하기

pool_pre_ping을 True로 설정하면 연결하기 전에 말 그대로 ping을 날려서 연결이 유효한지 확인한다. (SELECT 1)
유효하지 않으면 커넥션 풀에서 유효한 커넥션으로 가져와 재연결을 시도한다.

engine = create_engine("mysql+pymysql://user:pw@host/db", pool_pre_ping=True)


pool_recycle을 MySQL의 wait_timeout보다 작게 설정하기

문제 상황에서 오래 걸리는 요청이 있다고 언급했었는데, 작업 중간에 연결이 유효한지 주기적으로 확인하고 갱신할 필요가 있었다.
그래서 pool_recycle을 설정해줬으며 이때 MySQL의 wait_timeout 설정값 보다는 작아야 한다.
wait_timeout이 커넥션을 유지하는 최대 시간이니까 그 시간동안 주기적으로 체크하기 위함이다!

engine = create_engine(
    "mysql+pymysql://user:pw@host/db", pool_recycle=3600)
  • MySQL 설정은 my.cnf에서 설정했다.
  • wait_timeout의 기본값은 8시간이다.

wait_timeout을 늘리기

어떤 서비스인지에 따라 다르겠지만,wait_timeout이 너무 늘면 DB의 커넥션 풀을 고갈시킬 수 있어서 좋지 않다.
(일반적인 서비스라면 wait_timeout 값은 작게 설정하는게 옮다고 개인적으로 생각한다)
다만 트래픽이 적은 케이스이고 작업이 오래 걸린다면 wait_timeout을 늘리는 것도 필요하다.
wait_timeout을 줄인다면 오래 걸리는 작업은 완료되지 않으니까 말이다.

보통은 기본값인 8시간이면 다 처리될텐데 임의로 과도하게 줄여놓은 상황이라면 이 부분도 점검해보면 좋을 것 같다.

(참고) Flask-SQLAlchemy 설정

참고로 Flask-SQLAlchemy를 사용하고 있다면 SQLALCHEMY_POOL_RECYCLE 말고 SQLALCHEMY_ENGINE_OPTIONS를 설정하자.
SQLALCHEMY_POOL_RECYCLE는 v2.4에서 deprecated 되었다.

app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
    'pool': QueuePool(creator),
    'pool_size': 10,
    'pool_recycle': 120,
    'pool_pre_ping': True
}

References

카테고리:

업데이트:

댓글남기기