DbContextPool je nová funkce zavedená v ASP.NET Core 2.1, která šetří režijní zátěž při vytváření instance DbContext, ale je v ní skrytá malá jáma. Nedávno běžel nepřetržitě projekt ASP.NET Core po určitou dobu a poté se v logech objevila chyba, že databázový pool připojení dosáhl maximálního počtu připojení:
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) Nejdřív jsem si myslel, že je to nějaký kód, který způsobuje správné likvidace DbContextu, ale v kódu jsem žádné stopy nenašel. Později už nebylo o čem pochybovat, jen DbContextPool, takže jsem se pokusil DbContextPool odstranit, ale chyba zmizela. Bylo to skutečně způsobeno DbContextPool, ale co lidi nutí k otázce, je fakt, že DbContextPool byl původně určen k úspoře režie při vytváření instancí DbContextu, takže jak může spotřebovávat více databázových připojení a zátěž tohoto projektu je velmi nízká, jak může spotřebovávat celý pool připojení? O tomto zvláštním problému jsem dnes mluvil na týdenní schůzce a pak jsem najednou napadl, že každá instance DbContext bude zabírat připojení k databázi (SqlConnection), a když DbContextPool není povolen, jakmile požadavek skončí, příslušná instance DbContext bude odstraněna a připojení k databázi se vrátí zpět do poolu připojení. Při použití DbContextPool nebude DbContext vyřazen, ale po skončení požadavku bude vrácen zpět do DbContextPoolu a DbContext bude vrácen do vlastního poolu, což znamená, že jeho odpovídající databázové spojení nebude vráceno do poolu spojení, do kterého patří. Každý DbContext v DbContextPool odpovídá databázovému spojení a pro každý další DbContext v DbContextPool bude o jedno databázové spojení méně v databázovém poolu. Když jsou oba pooly různých velikostí a DbContextPool je větší než databázový connection pool, problém nastává, DbContextPool volně vyplní DbContext do poolu podle velikosti svého vlastního poolu (řekněme, že je 128), přičemž ignoruje velikost databázového connection poolu (za předpokladu, že je 100), a výše uvedená chyba nastane, když je 101. DbContext naplněn. Tento projekt používá výchozí nastavení, spouští výchozí nastavení tento problém?
Při pohledu na zdrojový kód implementace DbContextPool je výchozí limit velikosti pro discovery pool 128
Při pohledu na zdrojový kód implementace SqlConnention zjistíte, že výchozí limit velikosti pro pooly připojení je 100
Výchozí nastavení spustí problém, což je vlastně malá past.
S ohledem na důvod je řešení jednoduché: nastavte velikost poolSize DbContextPool na menší než Max_Pool_Size poolu připojení k databázi
|