DbContextPool er en ny funksjon introdusert i ASP.NET Core 2.1 som sparer for overhead ved å lage en DbContext-instans, men det er en liten grop skjult i den. Nylig kjørte et ASP.NET Core-prosjekt kontinuerlig i en periode, og så dukket det opp en feil i loggene om at databasetilkoblingspoolen nådde det maksimale antallet tilkoblinger:
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 jeg det var kode som fikk DbContext til å bli kvitt på riktig måte, men jeg fant ingen ledetråder i koden. Senere var det egentlig ingenting annet å tvile på, bare DbContextPool, så jeg prøvde å fjerne DbContextPool, men feilen forsvant. Det ble faktisk forårsaket av DbContextPool, men det som får folk til å lure er at DbContextPool opprinnelig var ment å spare overhead ved å lage DbContext-instanser, så hvordan kan det bruke flere databaseforbindelser, og belastningen på dette prosjektet er veldig lav, hvordan kan det bruke hele tilkoblingspoolen? Jeg snakket om dette merkelige problemet på det ukentlige møtet i dag, og så tenkte jeg plutselig at hver DbContext-instans vil ha en databasetilkobling (SqlConnection), og når DbContextPool ikke er aktivert, vil den tilsvarende DbContext-instansen bli kastet så snart forespørselen avsluttes, og databasetilkoblingen vil bli lagt tilbake i tilkoblingspoolen. Når DbContextPool brukes, vil ikke DbContext bli kastet, men vil bli lagt tilbake i DbContextPool etter at forespørselen er avsluttet, og DbContext vil bli plassert tilbake i sin egen pool, noe som betyr at den tilsvarende databasetilkoblingen ikke vil bli returnert til tilkoblingspoolen den tilhører. Hver DbContext i DbContextPool tilsvarer en databasetilkobling, og for hver ekstra DbContext i DbContextPool vil det være én mindre databasetilkobling i databasetilkoblingspoolen. Når de to poolene har ulik størrelse og DbContextPool er større enn databasetilkoblingspoolen, oppstår problemet: DbContextPool fyller fritt DbContext-poolen inn i poolen i henhold til størrelsen på sin egen pool (la oss si den er 128), ignorerer størrelsen på databasetilkoblingspoolen (forutsatt at den er 100), og feilen ovenfor vil oppstå når den 101. DbContext er fylt. Dette prosjektet bruker standardinnstillinger, utløser standardinnstillingen dette problemet?
Ser man på kildekoden til DbContextPool-implementeringen, er standardstørrelsesgrensen for oppdagelsespoolen 128
Hvis du ser på SqlConnention sin implementasjonskildekode, vil du finne at standardstørrelsesgrensen for tilkoblingspooler er 100
Standardinnstillingen vil utløse problemet, noe som egentlig er en liten fallgruve.
Når du kjenner årsaken, er løsningen enkel: sett poolSize til DbContextPool til mindre enn Max_Pool_Size av databasetilkoblingspoolen
|