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(Изключение 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
Стандартната настройка ще задейства проблема, което всъщност е малък капан (cap).
Като знаем причината, решението е просто – задайте poolSize на DbContextPool на по-малък от Max_Pool_Size на базата данни за връзки
|