Suivre les fuites de connection

Nous avons une application qui semble avoir des fuites de connection (SQL Server indique que la taille maximale du pool a été atteinte). Je suis seul sur ma machine de dev (évidemment), et juste en naviguant l'application, je triggers cette erreur. Le moniteur d'activité SQL Server montre un grand nombre de process utilisant ma database.

Je veux find quels files ouvrent des connections mais ne l'utilisent pas. Je pensais utiliser quelque chose comme grep pour, pour chaque file, countr le nombre de ".Open ()" et le nombre de ".Close ()", et get le file pour lequel les nombres ne sont pas égaux. Est-ce réalist?

Question bonus: les process trouvés dans SQL Server Activity Monitor correspondent-ils aux connections? Si non, comment puis-je savoir combien de connections sont ouvertes sur ma database?

L'application est dans asp.net (vb) 3.5, avec SQL Server 2005. Nous n'utilisons actuellement pas linq (encore) ou quelque chose comme ça.

Merci

Lorsque vous examinez le code du côté SQL Server, vous pouvez exécuter la requête suivante pour get une vue sur les dernières requêtes exécutées sur les connections en veille. (connections ouvertes qui ne font rien)

SELECT ec.session_id, last_read, last_write, text, client_net_address, program_name, host_process_id, login_name FROM sys.dm_exec_connections ec JOIN sys.dm_exec_sessions es ON ec.session_id = es.session_id CROSS APPLY sys.dm_exec_sql_text(ec.most_recent_sql_handle) AS dest where es.status = 'sleeping' 

Du côté de l'application, vous pouvez déboguer avec sos.dll comme décrit dans les articles suivants:

  • Comment faire pour résoudre fuite des objects SqlConnection Partie 1
  • Comment faire pour résoudre les objects SqlConnection divulgués Partie 2

Si vous avez besoin de plus d'informations sur comment utiliser windbg, ces articles sont une bonne intro:

  • Premiers pas avec WinDBG Partie 1
  • Premiers pas avec WinDBG Partie 2

La meilleure façon de lutter contre les fuites de connection est de le faire pendant les tests .

Vous pouvez utiliser un utilitaire automatisé pour que chaque test vérifie s'il y a une fuite de connection.

 @BeforeClass public static void initConnectionLeakUtility() { if ( enableConnectionLeakDetection ) { connectionLeakUtil = new ConnectionLeakUtil(); } } @AfterClass public static void assertNoLeaks() { if ( enableConnectionLeakDetection ) { connectionLeakUtil.assertNoLeaks(); } } 

Le ConnectionLeakUtil ressemble à ceci:

 public class ConnectionLeakUtil { private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE; private List idleConnectionCounters = Arrays.asList( H2IdleConnectionCounter.INSTANCE, OracleIdleConnectionCounter.INSTANCE, PostgreSQLIdleConnectionCounter.INSTANCE, MySQLIdleConnectionCounter.INSTANCE ); private IdleConnectionCounter connectionCounter; private int connectionLeakCount; public ConnectionLeakUtil() { for ( IdleConnectionCounter connectionCounter : idleConnectionCounters ) { if ( connectionCounter.appliesTo( Dialect.getDialect().getClass() ) ) { this.connectionCounter = connectionCounter; break; } } if ( connectionCounter != null ) { connectionLeakCount = countConnectionLeaks(); } } public void assertNoLeaks() { if ( connectionCounter != null ) { int currentConnectionLeakCount = countConnectionLeaks(); int diff = currentConnectionLeakCount - connectionLeakCount; if ( diff > 0 ) { throw new ConnectionLeakException( Ssortingng.format( "%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d", diff, connectionLeakCount, currentConnectionLeakCount ) ); } } } private int countConnectionLeaks() { try ( Connection connection = newConnection() ) { return connectionCounter.count( connection ); } catch ( SQLException e ) { throw new IllegalStateException( e ); } } private Connection newConnection() { try { return DriverManager.getConnection( jdbcProperties.getUrl(), jdbcProperties.getUser(), jdbcProperties.getPassword() ); } catch ( SQLException e ) { throw new IllegalStateException( e ); } } } 

Les implémentations IdleConnectionCounter peuvent être trouvées dans ce blog , et la version MySQL comme ceci:

 public class MySQLIdleConnectionCounter implements IdleConnectionCounter { public static final IdleConnectionCounter INSTANCE = new MySQLIdleConnectionCounter(); @Override public boolean appliesTo(Class<? extends Dialect> dialect) { return MySQL5Dialect.class.isAssignableFrom( dialect ); } @Override public int count(Connection connection) { try ( Statement statement = connection.createStatement() ) { try ( ResultSet resultSet = statement.executeQuery( "SHOW PROCESSLIST" ) ) { int count = 0; while ( resultSet.next() ) { Ssortingng state = resultSet.getSsortingng( "command" ); if ( "sleep".equalsIgnoreCase( state ) ) { count++; } } return count; } } catch ( SQLException e ) { throw new IllegalStateException( e ); } } } 

Maintenant, lorsque vous exécutez vos tests, vous obtenez un échec lors de la fuite d'une connection:

 :hibernate-core:test org.hibernate.jpa.test.EntityManagerFactoryClosedTest > classMethod FAILED org.hibernate.testing.jdbc.leak.ConnectionLeakException