DbContextPool은 ASP.NET Core 2.1에서 도입된 새로운 기능으로, DbContext 인스턴스를 생성하는 오버헤드를 줄여주지만, 그 안에는 작은 구멍이 숨겨져 있습니다. 최근에는 ASP.NET Core 프로젝트가 일정 기간 연속 실행되었고, 이후 데이터베이스 연결 풀이 최대 연결 수에 도달했다는 오류가 로그에 나타났습니다:
System.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. at System.Data.Common.ADP.ExceptionWithStackTrace(Exception e) 처음에는 DbContext가 제대로 폐기된 코드인 줄 알았지만, 코드에서 단서를 찾지 못했습니다. 나중에 의심할 만한 게 없고 DbContextPool만 남아 있었고, DbContextPool을 제거하려고 했지만 오류가 사라졌습니다. 실제로 DbContextPool 때문에 발생했지만, 사람들이 궁금해하는 점은 DbContextPool 원래 인스턴스 생성 부담을 줄이기 위해 설계된 것이기 때문인데, 어떻게 더 많은 데이터베이스 연결을 소모할 수 있겠습니까? 이 프로젝트의 부하가 매우 적은데 어떻게 전체 연결 풀을 소모할 수 있을까요? 오늘 주간 회의에서 이 이상한 문제에 대해 이야기했는데, 갑자기 각 DbContext 인스턴스가 데이터베이스 연결(SqlConnection)을 차지하고, DbContextPool이 활성화되지 않으면 요청이 끝나면 해당 DbContext 인스턴스가 폐기되고 데이터베이스 연결이 다시 연결 풀에 들어간다는 생각이 들었습니다. DbContextPool을 사용할 때, DbContext는 폐기되지 않고 요청이 종료된 후 다시 DbContextPool에 반환되며, DbContext는 자체 풀에 다시 배치되어 해당 데이터베이스 연결이 원래 속한 연결 풀로 반환되지 않습니다. DbContextPool의 각 DbContext는 데이터베이스 연결에 해당하며, DbContextPool에 추가된 DbContext마다 데이터베이스 연결 풀 내 연결이 하나 줄어듭니다. 두 풀의 크기가 다르고 DbContextPool이 데이터베이스 연결 풀보다 크면, 문제가 발생합니다. DbContextPool은 자신의 풀 크기(예: 128)에 따라 DbContext를 자유롭게 풀에 채우고, 데이터베이스 연결 풀의 크기(100이라고 가정)는 무시하며, 위의 오류가 101번째 DbContext가 채워질 때 발생합니다. 이 프로젝트는 기본 설정을 사용하는데, 기본 설정이 이 문제를 유발하나요?
DbContextPool 구현 소스 코드를 보면, 발견 풀의 기본 크기 제한은 128입니다
SqlConnention의 구현 소스 코드를 보면 연결 풀의 기본 크기 제한이 100입니다
기본 설정이 문제를 유발하는데, 사실 작은 함정입니다.
이유를 알게 된 해결책은 간단합니다. DbContextPool의 poolSize를 데이터베이스 연결 풀의 Max_Pool_Size보다 작게 설정하는 것입니다
|