DbContextPool je nova funkcija, uvedena v ASP.NET Core 2.1, ki prihrani stroške ustvarjanja DbContext instance, vendar je v njej skrita majhna jama. Nedavno je projekt ASP.NET Core tekel neprekinjeno določen čas, nato pa se je v dnevnikih pojavila napaka, da je bazen povezav v bazi podatkov dosegel največje število povezav:
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) Sprva sem mislil, da je neka koda povzročila, da se DbContext pravilno odstrani, vendar v kodi nisem našel nobenih sledi. Kasneje ni bilo več ničesar za dvomiti, le DbContextPool, zato sem poskušal odstraniti DbContextPool, a je napaka izginila. Res ga je povzročil DbContextPool, a kar ljudi sprašuje, je, da je bil DbContextPool prvotno namenjen prihrani stroškov ustvarjanja instanc DbContext, zato kako lahko porabi več povezav z bazo podatkov, in je obremenitev tega projekta zelo majhna, kako lahko porabi celoten bazen povezav? O tej nenavadni težavi sem danes govoril na tedenskem sestanku, nato pa sem nenadoma pomislil, da bo vsaka instanca DbContext zasedala povezavo z bazo podatkov (SqlConnection), in ko DbContextPool ni omogočen, se takoj po koncu zahteve ustrezna instanca DbContext odstrani, povezava z bazo podatkov pa se vrne v povezovalni bazen. Pri uporabi DbContextPool DbContext ne bo odstranjen, ampak bo po koncu zahteve vrnjen v DbContextPool, DbContext pa se vrne v svoj lasten bazen, kar pomeni, da ustrezna povezava z bazo podatkov ne bo vrnjena v povezovalni bazen, kateremu pripada. Vsak DbContext v DbContextPoolu ustreza povezavi z bazo podatkov, in za vsak dodatni DbContext v DbContextPoolu bo ena povezava z bazo podatkov manj. Ko sta oba bazena različnih velikosti in je DbContextPool večji od povezovalnega bazena podatkovne baze, nastane težava: DbContextPool prosto napolni DbContext v bazen glede na velikost svojega bazena (recimo, da je 128), pri čemer ignorira velikost povezovalnega bazena baze podatkov (ob predpostavki, da je 100), zgornja napaka pa se pojavi, ko je 101. DbContext polnjen. Ta projekt uporablja privzete nastavitve, ali privzeta nastavitev sproži ta problem?
Če pogledamo izvorno kodo implementacije DbContextPool, je privzeta omejitev velikosti za discovery pool 128
Če pogledate izvorno kodo implementacije SqlConnentiona, boste ugotovili, da je privzeta omejitev velikosti za povezave 100
Privzeta nastavitev sproži težavo, kar je pravzaprav majhna past.
Ker poznamo razlog, je rešitev preprosta: nastavite velikost bazena DbContextPool na manjšo od Max_Pool_Size bazena povezav z bazo podatkov
|