DbContextPool är en ny funktion som introducerades i ASP.NET Core 2.1 som sparar överhuvudet att skapa en DbContext-instans, men det finns en liten grop dold i den. Nyligen kördes ett ASP.NET Core-projekt kontinuerligt under en period och sedan dök ett fel upp i loggarna om att databasens anslutningspool nådde det maximala antalet anslutningar:
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. på System.Data.Common.ADP.ExceptionWithStackTrace(Exception e) Först trodde jag att det var någon kod som gjorde att DbContext skulle tas bort korrekt, men jag hittade inga ledtrådar i koden. Senare fanns det egentligen inget annat att tvivla på, bara DbContextPool, så jag försökte ta bort DbContextPool, men felet försvann. Det orsakades faktiskt av DbContextPool, men det som får folk att undra är att DbContextPool ursprungligen var avsett att spara överhead för att skapa DbContext-instanser, så hur kan det förbruka fler databasanslutningar, och belastningen på detta projekt är mycket låg, hur kan det då förbruka hela anslutningspoolen? Jag pratade om detta märkliga problem på det veckovisa mötet idag, och plötsligt tänkte jag att varje DbContext-instans kommer att uppta en databasanslutning (SqlConnection), och när DbContextPool inte är aktiverad, så snart begäran avslutas, kommer motsvarande DbContext-instans att kasseras och databasanslutningen kommer att läggas tillbaka i anslutningspoolen. När DbContextPool används kommer inte DbContext att kasseras utan placeras tillbaka i DbContextPool efter att begäran avslutats, och DbContext placeras tillbaka i sin egen pool, vilket innebär att dess motsvarande databasanslutning inte kommer att återlämnas till den anslutningspool den tillhör. Varje DbContext i DbContextPool motsvarar en databasanslutning, och för varje ytterligare DbContext i DbContextPool finns det en mindre databasanslutning i databasanslutningspoolen. När de två poolerna är av olika storlek och DbContextPool är större än databasanslutningspoolen uppstår problemet: DbContextPool fyller fritt DbContexten i poolen enligt storleken på sin egen pool (låt oss säga att den är 128), ignorerar storleken på databasanslutningspoolen (förutsatt att den är 100), och ovanstående fel uppstår när den 101:a DbContexten fylls. Det här projektet använder standardinställningar, utlöser standardinställningen detta problem?
Om man tittar på DbContextPools implementationskällkod är standardstorleksgränsen för upptäcktspoolen 128
Om du tittar på SqlConnentions implementationskällkod kommer du att se att standardstorleksgränsen för anslutningspooler är 100
Standardinställningen utlöser problemet, vilket egentligen är en liten fallgrope.
Med vetskap om orsaken är lösningen enkel: sätt poolSize för DbContextPool till mindre än databasens anslutningspools Max_Pool_Size
|