2011-09-27 7 views
9

NHibernate는 ADO.NET 데이터베이스 연결을 풀링하지 않는다. 연결은 트랜잭션이 커밋되거나 롤백 될 때만 닫힙니다. 소스 코드를 살펴보면 NHibernate가 ISession이 폐기 될 때 연결을 닫을 수 있도록 NHibernate를 구성 할 방법이 없다는 것을 알 수 있습니다.NHibernate와 ADO.NET 연결 풀링

이 동작의 목적은 무엇입니까? ADO.NET에는 연결 풀링 자체가 있습니다. 트랜잭션 내에서 항상 열어 둘 필요는 없습니다. 이 동작으로 인해 불필요하게 분산 된 트랜잭션이 생성됩니다. 따라서 http://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/에 설명 된 가능한 해결 방법은 작동하지 않습니다 (적어도 NHibernate 3.1.0에는 포함되지 않음). 나는 Informix를 사용하고 있습니다. 다른 모든 데이터베이스 (NHibernate Connection Pooling)에 대해서도 동일한 문제가 발생합니다.

이 문제를 피하는 다른 해결 방법이나 조언이 있습니까? 여기

문제를 재현 단위 테스트입니다 :

[Test] 
    public void DoesNotCloseConnection() 
    { 
    using (SessionFactoryCache sessionFactoryCache = new SessionFactoryCache()) 
    { 
     using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromMinutes(10) })) 
     { 
      fixture.Setup(); // Creates test data 

      System.Data.IDbConnection connectionOne; 
      System.Data.IDbConnection connectionTwo; 

      using (ISessionFactory sessionFactory = sessionFactoryCache.CreateFactory(GetType(), new TestNHibernateConfigurator())) 
      { 
       using (ISession session = sessionFactory.OpenSession()) 
       { 
       var result = session.QueryOver<Library>().List<Library>(); 
       connectionOne = session.Connection; 
       } 
      } 

      // At this point the first IDbConnection used internally by NHibernate should be closed 

      using (ISessionFactory sessionFactory = sessionFactoryCache.CreateFactory(GetType(), new TestNHibernateConfigurator())) 
      { 
       using (ISession session = sessionFactory.OpenSession()) 
       { 
       var result = session.QueryOver<Library>().List<Library>(); 
       connectionTwo = session.Connection; 
       } 
      } 

      // At this point the second IDbConnection used internally by NHibernate should be closed 

      // Now two connections are open because the transaction is still running 
      Assert.That(connectionOne.State, Is.EqualTo(System.Data.ConnectionState.Closed)); // Fails because State is still 'Open' 
      Assert.That(connectionTwo.State, Is.EqualTo(System.Data.ConnectionState.Closed)); // Fails because State is still 'Open' 
     } 
    } 
    } 

우리가

SessionImpl.cs 트랜잭션 여전히 이후 NHibernate에-세션의 폐기는 아무것도 실시하지 않습니다 :

public void Dispose() 
    { 
     using (new SessionIdLoggingContext(SessionId)) 
     { 
      log.Debug(string.Format("[session-id={0}] running ISession.Dispose()", SessionId)); 
      if (TransactionContext!=null) 
      { 
       TransactionContext.ShouldCloseSessionOnDistributedTransactionCompleted = true; 
       return; 
      } 
      Dispose(true); 
     } 
    } 

가 ConnectionProvider를 호출하는 ConnectionManager에는 다음과 같은 몇 가지 전제 조건이 있으므로 사용자 정의 ConnectionProvider를 삽입해도 작동하지 않습니다. 거래는 허용되지 않습니다.

ConnectionManager.cs :

public IDbConnection Disconnect() { 
     if (IsInActiveTransaction) 
      throw new InvalidOperationException("Disconnect cannot be called while a transaction is in progress."); 

     try 
     { 
      if (!ownConnection) 
      { 
       return DisconnectSuppliedConnection(); 
      } 
      else 
      { 
       DisconnectOwnConnection(); 
       ownConnection = false; 
       return null; 
      } 
     } 
     finally 
     { 
      // Ensure that AfterTransactionCompletion gets called since 
      // it takes care of the locks and cache. 
      if (!IsInActiveTransaction) 
      { 
       // We don't know the state of the transaction 
       session.AfterTransactionCompletion(false, null); 
      } 
     } 
    } 
+0

는 지금까지 내가 아는 한, 데이터베이스는 트랜잭션을 사용하기 위해 같은 연결이 필요합니다. 따라서 트랜잭션이 실행되는 동안 연결을 유지한다는 것이 이상하지 않은가? 연결이 풀로 반환되면 두 번째 풀에서 동일한 연결을받을 수 있도록하는 것이 없습니다. – jishi

+0

그러나 구체적인 테스트에서는 ADO.NET의 일부인 기본 IdbConnection을 검사하고 있으며이 경우 테스트중인 ADO.NET 연결 풀링이 아닌지 확인합니다. 당신이해야 할 일은 똑같은 팩토리에서 두 개의 다른 세션을 만들고 똑같은 연결을 받는지 확인하는 것입니다. – jishi

+0

각 트랜잭션의 시작 부분에서 Driver 클래스 (내 경우 OdbcDriver)가 새 DbConnection (OdbcConnection)을 만듭니다. 이 연결은 불필요한 전체 트랜잭션을 그대로 유지합니다. 필자가 작성한 테스트는 실제로 하나의 SessionFactory에서 두 개의 다른 세션을 사용합니다. – Antineutrino

답변

8

NHibernate에 두 가지 "모드"가 있습니다.

  • 응용 프로그램에서 연결을 열면 해당 응용 프로그램을 관리 할 수 ​​있습니다. 이 "모드"는 sessionfactory.OpenSession(connection)에 연결을 전달할 때 사용됩니다.
  • 또는 연결이 NH에 의해 생성되었습니다. 그런 다음 세션이 닫히면 닫힙니다. 이 "모드"는 연결을 건널 때 사용됩니다. sessionfactory.OpenSession()

TransactionScope에 대한 지원이 있습니다. 아마도 첫 번째 "모드"를 사용하고있을 것입니다. 아마 연결은 뉴 햄프셔에 의해서가 아니라 거래 범위에 의해 보류 될 것입니다. 정확히 모르겠지만 환경 거래는 사용하지 않습니다.

NH 입니다. 그런데 ADO.NET 연결 풀을 사용하면됩니다.

ISession.Disconnect()을 사용하여 세션 연결을 끊고 ISession.Reconnect()을 사용하여 다시 연결할 수도 있습니다. 에서

documentation 찾을 :

방법 ISession.Disconnect()는 ADO.NET 연결에서 세션을 분리하고 (당신 연결을 제공하지 않는 한) 연결을 풀 (pool)로 반환됩니다.

+1

답장을 보내 주셔서 감사합니다.하지만 제 2의 모드가 잘못되었다고 생각합니다.세션이 삭제되면 연결이 열린 상태로 유지됩니다. snipped 코드에서 볼 수 있듯이 (ConnectionManager.cs) 위에 게시 했으므로 트랜잭션 범위 내에서 연결을 끊거나 다시 연결할 수 없습니다. 제공 한 링크에서 다시 연결 해제/다시 연결하기 전에 먼저 트랜잭션을 커밋/중단해야한다고합니다. 연결 (IDbConnection)은 ConnectionManager 클래스에 의해 트랜잭션이 아닌 개인 멤버로 제어됩니다. – Antineutrino

0

연결 문자열에 다음 설정을 추가하여이 작업을 수행 할 수 있습니다.

Pooling=true; 
Min Pool Size=3; 
Max Pool Size=25; 
Connection Lifetime=7200; 
Connection Timeout=15; 
Incr Pool Size=3; 
Decr Pool Size=5; 

풀링 : 앱이

최소 풀 풀링 가능 : 모든 세션이 종료 될 때 연결의 최소 수도 열어 둘 수 있습니다.

최대 풀 : 앱이 DB에 대해 열어주는 최대 연결 수입니다. 최대 값에 도달하면 Connection Timeout에 지정된 시간 (초) 동안 대기 한 다음 예외를 throw합니다.

연결 시간 제한 (초)에서 최대 시간 풀에서 사용 가능한 연결을 기다리는

연결 수명 : 연결이 풀에 반환 될 때, 그것의 생성 시간은 현재 시간과 비교하고있다 해당 시간 범위 (초)가 연결 수명으로 지정된 값을 초과하면 연결이 끊어집니다. 영 (0) 값은 풀링 된 연결이 최대 연결 시간 초과를 갖도록합니다.

Incr 풀 크기 : 모든 연결을 사용할 때 설정되는 연결 수를 제어합니다.

Decr 풀 크기 : 설정된 연결을 과도하게 사용하지 않을 때 닫히는 연결 수를 제어합니다.

http://www.codeproject.com/Articles/17768/ADO-NET-Connection-Pooling-at-a-Glance