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. на 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 пулу з'єднань бази даних
|