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, так как он может потреблять больше соединений к базе данных, а нагрузка этого проекта очень низкая, как он может потреблять весь пул соединений? Сегодня я говорил об этой странной проблеме на еженедельном собрании, и вдруг подумал, что каждый экземпляр DbContext будет занимать соединение с базой данных (SqlConnection), и когда DbContextPool не включён, как только запрос завершится, соответствующий экземпляр DbContext будет уничтожен, а соединение с базой данных возвращается в пул соединений. При использовании DbContextPool DbContext не утилизируется, а после завершения запроса возвращается в DbContextPool, а DbContext возвращается в отдельный пул, что означает, что соответствующее соединение с базой данных не возвращается в пул соединений, к которому он принадлежит. Каждый DbContext в DbContextPool соответствует соединению с базой данных, и для каждого дополнительного DbContext в DbContextPool в пуле соединений базы данных будет на одно соединение с базой данных. Когда два пула разного размера, а DbContextPool больше пула соединений базы данных, возникает проблема: DbContextPool свободно заполняет DbContext в пул в зависимости от размера собственного пула (допустим, он 128), игнорируя размер пула соединений базы данных (если он 100), и вышеописанная ошибка возникает, когда заполнён 101-й DbContext. В этом проекте используются настройки по умолчанию, вызывает ли она такую проблему?
Смотря на исходный код реализации DbContextPool, ограничение по умолчанию для пула обнаружения составляет 128
Если посмотреть исходный код реализации SqlConnention, можно увидеть, что по умолчанию лимит размера для пулов соединений — 100
Стандартная настройка вызовет проблему, а это на самом деле небольшая ошибка.
Зная причину, решение простое — установить poolSize DbContextPool меньше Max_Pool_Size пула соединений базы данных
|