상세 컨텐츠

본문 제목

C#으로 Transactional Object에 Object Pooling을 사용하도록 구성하기

C#

by 탑~! 2008. 4. 1. 10:57

본문

Transactional Object에 Object Pooling을 사용하도록 구성하기 위해서는 다음의 요구사항을 반드시 지켜야합니다.

1. manually enlist
2. resource manager의 automatic enlist는 turn off
3. IObjectControl interface를 구현

우선적으로 Object Pooling은 JITA와 같이 사용하면 좋은 성능을 낼 수 있죠.
하지만, 굳이 JITA를 사용하지 않는 다고 해도 구현을 할 수 있습니다. (특별히 그렇게 해야할 필요가 있다면 말이죠.)
Object Pooling을 위한 요구사항을 조금 언급한다면,

1. JITA
2. stateless
3. Thread Affinity를 금지

를 들 수 있습니다. 종종, 이를 무시하고 개발한다고 해서 크게 문제될 것이 없어 보이지만,
성능적인 면이나 scalabilty 면에서 이를 무시하고 Object Pooling을 쓸 이유가 없습니다.
반드시 지켜달라는 겁니다.

그럼, 구현은 다음과 같이 하면 됩니다.

Activate() 함수에는 manually하게 Transaction을 Enlist 합니다.

protected override void Activate() {
m_con.EnlistDistributedTransaction
((ITransaction)ContextUtil.Transaction);
base.Activate();
}


이는 중요합니다. 만일, 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 합니다.

protected override void Deactivate() {
m_con.EnlistDistributedTransaction (null);
base.Deactivate ();
}


Method를 하나 만들면, 다음과 같이 할 것 입니다.

[AutoComplete]
public void ExeQuery()
{
  // 생략
}


그리고, 빠지지 말아야 할 것은 CanBePooled()를 적절히 true or false를 return 합니다. true는 Transaction안에 있다는 것이고, false는 Pooled에 return 되는 것을 의미합니다.

protected override bool CanBePooled() {
if (m_con.State != System.Data.ConnectionState.Open)
{
return false;
}
else
{
return true;
}
}


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 이므로 그렇죠)

protected override bool CanBePooled()
{
return true;
}



Enlist ADO.NET Connections in Pooled Objects Manually (
http://www.ftponline.com/vsm/2004_05/online/esabbadin/) 에서 와 C# COM+ programming

출처 : http://byung.egloos.com/2077442

'C#' 카테고리의 다른 글

Bulk Insert into SQL from C# App  (0) 2008.06.18
.Net 2.0 원격 DB 디버깅  (0) 2008.04.04
진법변환  (0) 2008.04.01
Provider Infomation - SQL 수행 내용 확인 방법  (0) 2008.04.01
비스타에서 설치 프로젝트 권한상승 하기  (0) 2008.04.01

관련글 더보기