DbContextPool je nová funkcia predstavená v ASP.NET Core 2.1, ktorá šetrí režijné náklady na vytvorenie inštancie DbContext, ale je v nej skrytá malá jama. Nedávno projekt ASP.NET Core bežal nepretržite určitý čas a potom sa v logoch objavila chyba, že databázový connection pool dosiahol maximálny počet pripojení:
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) Najprv som si myslel, že je to nejaký kód, ktorý spôsobuje správne likvidáciu DbContextu, ale nenašiel som v kóde žiadne stopy. Neskôr už naozaj nebolo čo pochybovať, len DbContextPool, tak som sa pokúsil DbContextPool odstrániť, ale chyba zmizla. Skutočne to spôsobil DbContextPool, ale čo ľudí núti premýšľať, je, že DbContextPool bol pôvodne určený na šetrenie režijných nákladov pri vytváraní inštancií DbContext, takže ako môže spotrebovať viac databázových pripojení, keď je záťaž tohto projektu veľmi nízka, ako môže spotrebovať celý pool pripojení? O tomto zvláštnom probléme som dnes hovoril na týždennom stretnutí a potom som si zrazu myslel, že každá inštancia DbContext bude obsadzovať databázové pripojenie (SqlConnection), a keď DbContextPool nie je povolený, hneď ako požiadavka skončí, príslušná inštancia DbContext bude zlikvidovaná a databázové spojenie sa vráti späť do connection poolu. Pri použití DbContextPool nebude DbContext vyradený, ale po skončení požiadavky sa vráti späť do DbContextPoolu a DbContext sa vráti do vlastného poolu, čo znamená, že jeho príslušné databázové spojenie sa nevráti do poolu, ku ktorému patrí. Každý DbContext v DbContextPool zodpovedá databázovému pripojeniu a pre každý ďalší DbContext v DbContextPool bude v databázovom poole o jedno databázové pripojenie menej. Keď sú oba pooly rôznej veľkosti a DbContextPool je väčší ako databázový connection pool, problém nastáva – DbContextPool voľne naplní DbContext do poolu podľa veľkosti svojho vlastného poolu (povedzme, že je 128), pričom ignoruje veľkosť databázového connection poolu (za predpokladu, že je 100), a vyššie uvedená chyba nastane, keď je 101. DbContext naplnený. Tento projekt používa predvolené nastavenia, spôsobuje predvolené nastavenie tento problém?
Pri pohľade na zdrojový kód implementácie DbContextPool je predvolený limit veľkosti pre discovery pool 128
Keď sa pozriete na zdrojový kód implementácie SqlConnention, zistíte, že predvolený limit veľkosti pre pooly pripojení je 100
Predvolené nastavenie spustí problém, čo je vlastne malá pasca.
Keďže poznáme dôvod, riešenie je jednoduché: nastavte veľkosť poolSize DbContextPool na menšiu Max_Pool_Size databázového connection poolu
|