DbContextPool ist eine neue Funktion, die in ASP.NET Core 2.1 eingeführt wurde und den Aufwand für die Erstellung einer DbContext-Instanz erspart, aber darin ist eine kleine Grube versteckt. Kürzlich lief ein ASP.NET Core-Projekt über einen bestimmten Zeitraum kontinuierlich, und dann erschien in den Logs ein Fehler, dass der Datenbank-Verbindungspool die maximale Anzahl an Verbindungen erreicht hat:
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. bei System.Data.Common.ADP.ExceptionWithStackTrace(Exception e) Zuerst dachte ich, es sei ein Code, der den DbContext ordnungsgemäß entsorgt hat, aber ich habe keine Hinweise im Code gefunden. Später gab es wirklich keinen weiteren Zweifel, nur DbContextPool, also versuchte ich, DbContextPool zu entfernen, aber der Fehler verschwand. Es wurde tatsächlich durch DbContextPool verursacht, aber was viele fragen lässt, ist, dass DbContextPool ursprünglich dazu gedacht ist, den Overhead der Erstellung von DbContext-Instanzen zu sparen, also wie kann es mehr Datenbankverbindungen verbrauchen, und die Last dieses Projekts ist sehr gering, wie kann es den gesamten Verbindungspool verbrauchen? Ich habe heute beim wöchentlichen Treffen über dieses seltsame Problem gesprochen und dann plötzlich gemerkt, dass jede DbContext-Instanz eine Datenbankverbindung (SqlConnection) belegt, und wenn DbContextPool nicht aktiviert ist, wird die entsprechende DbContext-Instanz nach Ende der Anfrage entsorgt und die Datenbankverbindung wird wieder in den Connection Pool zurückgelegt. Bei Verwendung von DbContextPool wird der DbContext nicht entsorgt, sondern nach Ablauf der Anfrage wieder in den DbContextPool zurückgelegt, und der DbContext wird wieder in seinen eigenen Pool gelegt, was bedeutet, dass die entsprechende Datenbankverbindung nicht an den Verbindungspool zurückgegeben wird, zu dem er gehört. Jeder DbContext im DbContextPool entspricht einer Datenbankverbindung, und für jeden zusätzlichen DbContext im DbContextPool gibt es eine Datenbankverbindung weniger im Datenbankverbindungspool. Wenn die beiden Pools unterschiedlich groß sind und der DbContextPool größer ist als der Datenbank-Connection Pool, entsteht das Problem: DbContextPool füllt den DbContext frei in den Pool entsprechend der Größe seines eigenen Pools (nehmen wir an, er beträgt 128), ignoriert dabei die Größe des Datenbank-Connection-Pools (angenommen, sie beträgt 100), und der oben genannte Fehler tritt auf, wenn der 101. DbContext gefüllt ist. Dieses Projekt verwendet die Standardeinstellungen, löst die Standardeinstellung dieses Problem aus?
Betrachtet man den Quellcode der DbContextPool-Implementierung, beträgt die Standardgrößenbegrenzung für den Discovery Pool 128
Wenn man sich den Quellcode der Implementierung von SqlConnention ansieht, stellt man fest, dass die Standardgrößenbegrenzung für Verbindungspools 100 beträgt
Die Standardeinstellung löst das Problem aus, was wirklich eine kleine Falle ist.
Wenn man den Grund kennt, ist die Lösung einfach: Setze die poolSize des DbContextPool auf weniger als die Max_Pool_Size des Datenbank-Verbindungspools
|