우선적으로 Object Pooling은 JITA와 같이 사용하면 좋은 성능을 낼 수 있죠. 하지만, 굳이 JITA를 사용하지 않는 다고 해도 구현을 할 수 있습니다. (특별히 그렇게 해야할 필요가 있다면 말이죠.) Object Pooling을 위한 요구사항을 조금 언급한다면,
1. JITA 2. stateless 3. Thread Affinity를 금지
를 들 수 있습니다. 종종, 이를 무시하고 개발한다고 해서 크게 문제될 것이 없어 보이지만, 성능적인 면이나 scalabilty 면에서 이를 무시하고 Object Pooling을 쓸 이유가 없습니다. 반드시 지켜달라는 겁니다.
이는 중요합니다. 만일, Automatic하게 enlist 한다는 가정하에 Pool size가 1라는 특별한 상황에서 설명하면
1. Client 1이 Pooled object의 Method Call을 합니다. 이때 Transaction ID는 1234 라고 가정합니다. 2. Pooled Object는 Transaction ID 1234를 갖고 database를 Open합니다. 3. Method Call이 종료되면, 해당 Object는 pool로 갑니다. 4. Client 2이 Pooled Object의 Method Call을 합니다. 이때 Transaction ID는 4321라고 가정합니다. 5. Pooled Object는 이전의 Database Connection을 reuse할 수 있습니다. 5.1 결국, Transaction ID 4321를 가지고 기존의 Transaction에 자동으로 enlist됩니다. 5.2 이로 인해 Exception이 발생하고 Transaction Abort가 유발될 수 있습니다.
이와같은 시나리오가 나올 수 있습니다. Manually 하게 Transaction에 enlist한다는 것은 이러한 부분의 Check가 가능하다는 것이며, 이 bad scenario에서 새로운 transaction에 enlist가 되도록 할 수 있다는 의미입니다. 그리고, Constructor에서 DB를 Open합니다. 이때, Connection String에 Enlist=false를 줌으로써 Autoenlist를 turn off 할 수 있습니다.
[JustInTimeActivation(true)] [Transaction(TransactionOption.Required), ObjectPooling(true,3,30), public class myclass : ServicedComponent { private SqlConnection m_con ; public myclass(){ m_con = new SqlConnection( "Password=whatever;User ID=sa;Initial Catalog=pubs;Enlist=false") m_con.Open (); }
Unenlist를 위해서는 Deactivate 함수를 다음과 같이 override 합니다.
Object 의 Close나 dispose의 구현은 중요합니다만, Object Pooling에서는 적당치 않는 것 같습니다. Design 상으로 유추해보면, Client가 Dispose Call을 하지 않아도, GC가 Object를 Collect 할 것이며, 이는 Pool로 return 된 후에 reuse가 될 것입니다. 만일, NonPooled Object일 경우는 어떻게 할까요? 앞서서 본 Pooled Object 구현시 구현된 Constructor, Activate function안의 code는 다 제거합니다. 그리고, 단지, Method안에서 DBConnection 및 connection.Close(), Command.Dispose() 까지 호출하도록 해야 합니다. NonPooled Object Method 안에서 Close, Dispose가 호출되지 않으면, Connection Leak이 발생할 수 있습니다. 주의해야 합니다.
[AutoComplete] public void ExeNonPooledObjectQuery() { m_con = new SqlConnection(m_ConnectionString) ; m_cmd = new SqlCommand() ; //. . . m_con.Open() ; m_cmd.ExecuteNonquery() ; m_con.Close() ; m_cmd.Dispose() ; }
그리고CanBePooled는 false 를 return 합니다. (당연히 NonPooled 이므로 그렇죠)