DbContextPool is een nieuwe functie die is geïntroduceerd in ASP.NET Core 2.1 en die de overhead bespaart van het maken van een DbContext-instantie, maar er zit een klein putje in verborgen. Onlangs draaide een ASP.NET Core-project gedurende een bepaalde periode continu en verscheen er een fout in de logs dat de database-verbindingspool het maximale aantal verbindingen had bereikt:
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. op System.Data.Common.ADP.ExceptionWithStackTrace(Exception e) In eerste instantie dacht ik dat het code was die ervoor zorgde dat de DbContext correct werd verwijderd, maar ik vond geen aanwijzingen in de code. Later was er verder niets meer om aan te twijfelen, alleen DbContextPool, dus probeerde ik DbContextPool te verwijderen, maar de fout verdween. Het werd inderdaad veroorzaakt door DbContextPool, maar wat mensen doet afvragen is dat DbContextPool oorspronkelijk bedoeld is om de overhead van het aanmaken van DbContext-instanties te besparen, dus hoe kan het meer databaseverbindingen verbruiken, en de belasting van dit project is erg laag, hoe kan het dan de hele verbindingspool verbruiken? Ik sprak vandaag tijdens de wekelijkse vergadering over dit vreemde probleem, en toen dacht ik ineens dat elke DbContext-instantie een databaseverbinding (SqlConnection) zal bezetten, en wanneer DbContextPool niet is ingeschakeld, zodra het verzoek eindigt, wordt de bijbehorende DbContext-instantie verwijderd en wordt de databaseverbinding weer teruggeplaatst in de verbindingspool. Bij gebruik van DbContextPool wordt de DbContext niet verwijderd, maar wordt hij na afloop van het verzoek weer in de DbContextPool geplaatst, en de DbContext wordt weer in een eigen pool geplaatst, wat betekent dat de bijbehorende databaseverbinding niet wordt teruggegeven aan de verbindingspool waartoe het behoort. Elke DbContext in de DbContextPool komt overeen met een databaseverbinding, en voor elke extra DbContext in de DbContextPool is er één minder databaseverbinding in de databaseverbindingspool. Wanneer de twee pools van verschillende grootte zijn en de DbContextPool groter is dan de databaseverbindingspool, ontstaat het probleem: DbContextPool vult de DbContext vrijelijk in de pool volgens de grootte van zijn eigen pool (stel dat het 128 is), negeert de grootte van de databaseverbindingspool (ervan uitgaande dat die 100 is), en de bovenstaande fout zal optreden wanneer de 101e DbContext is gevuld. Dit project gebruikt de standaardinstellingen, veroorzaakt de standaardinstelling dit probleem?
Als je kijkt naar de broncode van de implementatie van DbContextPool, is de standaardlimiet voor de discovery pool 128
Als je kijkt naar de implementatiebroncode van SqlConnention, zie je dat de standaardlimiet voor verbindingspools 100 is
De standaardinstelling veroorzaakt het probleem, wat eigenlijk een klein valkuil is.
Als je de reden weet, is de oplossing eenvoudig: stel de poolSize van de DbContextPool kleiner dan de Max_Pool_Size van de databaseverbindingspool
|