DbContextPool è una nuova funzionalità introdotta in ASP.NET Core 2.1 che risparmia il sovraccarico di creare un'istanza DbContext, ma al suo interno c'è un piccolo fosso. Recentemente, un progetto ASP.NET Core è stato eseguito ininterrottamente per un certo periodo di tempo e poi è apparso un errore nei log che indicava che il pool di connessione al database aveva raggiunto il numero massimo di connessioni:
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. su System.Data.Common.ADP.ExceptionWithStackTrace(Exception e) All'inizio pensavo fosse un codice a causare lo smaltimento corretto di DbContext, ma non ho trovato indizi nel codice. Più tardi, non c'era più nulla di cui dubitare, solo DbContextPool, così ho provato a rimuovere DbContextPool, ma l'errore è sparito. È stato effettivamente causato da DbContextPool, ma ciò che fa riflettere è che DbContextPool è stato originariamente pensato per risparmiare il sovraccarico di creare istanze di DbContext, quindi come può consumare più connessioni al database, e il carico di questo progetto è molto basso, come può consumare l'intero pool di connessioni? Ho parlato di questo strano problema durante la riunione settimanale di oggi, e poi improvvisamente ho pensato che ogni istanza di DbContext occuperà una connessione al database (SqlConnection), e quando DbContextPool non è abilitato, non appena la richiesta termina, l'istanza corrispondente di DbContext verrà eliminata e la connessione al database verrà riinserita nel pool di connessioni. Quando si utilizza DbContextPool, il DbContext non verrà eliminato ma reinserito nel DbContextPool dopo la fine della richiesta, e il DbContext verrà reinserito nel proprio pool, il che significa che la corrispondente connessione al database non sarà restituita al pool di connessione a cui appartiene. Ogni DbContext nel DbContextPool corrisponde a una connessione al database, e per ogni DbContext aggiuntivo nel DbContextPool, ci sarà una connessione a database in meno nel pool di connessioni del database. Quando i due pool sono di dimensioni diverse e il DbContextPool è più grande del pool di connessione al database, sorge il problema: DbContextPool riempie liberamente il DbContext nel pool in base alla dimensione del proprio pool (diciamo che sia 128), ignorando la dimensione del pool di connessione al database (supponendo che sia 100), e l'errore sopra si verifica quando il 101° DbContext viene riempito. Questo progetto usa le impostazioni predefinite, l'impostazione predefinita scatena questo problema?
Guardando il codice sorgente per l'implementazione di DbContextPool, il limite di dimensione predefinito per il discovery pool è 128
Guardando il codice sorgente di implementazione di SqlConnention, scoprirai che il limite di dimensione predefinito per i pool di connessione è 100
L'impostazione predefinita farà scattare il problema, che in realtà è un piccolo problema.
Conoscendo il motivo, la soluzione è semplice: impostare la dimensione del pool del DbContextPool a meno del Max_Pool_Size del pool di connessione al database
|