블로그 이미지
fiadot_old

칼퇴근을 위한 게임 서버 개발 방법론에 대한 심도있는 고찰 및 성찰을 위한 블로그!

Rss feed Tistory
Technical Article 2007. 12. 27. 03:03

SP Dispatcher에 대해...

어느 정도 네트웍 엔진의 골격을 잡고 최근 DB프로그래밍쪽에 많은 시간을 할애하고 있다.

Cache DB로 완전한 전환을 하기 전에 구현된 SP(Stored Procedure)들에 대해 스키마를 얻어오고 그 정보를 토대로 ATL Consumer Wizard의 Accessor 생성기와 유사한 형태의 코드 제네레이터를 만들었다.

sp이름을 통해 parameter와 column의 타입, 이름, 길이를 얻어서 생성하고 맵핑까지 끝냈는데... 앗싸 좋쿠나 하고 있던 찰라 문제가 발생했다.
 
insert와 update에 대해서 까지 미쳐 생각이 닿지 못했다.

PK가 먹어 있는 테이블에 insert를 더미값으로 집어넣으니 integratviloation이 뜰수밖에 ㅡ_ㅡ;


초기에 생각했던 sp구조 데이터를 토대로 boost::spirit을 이용해서 파싱해서 생성하는 방법으로 돌아가게됐다. 대신에 기존의 DBA가 작업하던 엑셀 시트에서 약간의 변형된 형태로 사용하는 구조로 번거로움을 줄이면서 작업하는 방향으로 합의를 봤다..


지금 시간 새벽 3시 ...

4시까지 하면 다 할수 있을꺼 같은데 ...^^;
,
Technical Article 2007. 9. 28. 22:58

OLEDB의 Connection Pool에 대한 기본값 사용시 4가지 방법에 대한 수행속도 비교

MSSQL2K와 OLEDB Provider를 사용하여 VS6기반 ATL을 사용하여

OLEDB Wrapper를 구성해서 게임용 DB서버에서 사용하고 있었다.


올 중순부터  VS2005로 넘어오고 MSSQL2005로 바꾸면서,

기존의 네트워크 모듈부분을 뜯어고치면서

부분적으로 COM으로 구성하던 중,

DB쪽을 손보면서 OLEDB Wrapper에 대해 코드 리뷰를 하게 되었다.


기존 구조에서는 DBMS와의 연결은 단일하게 하고

Session을 DataSource에서 얻어와서 Accessor에서 사용하는 방식이였다.

즉, Stored Procedure나 query가 호출될때 session을 새로 생성하는 방식이다.

당시에는 MSSQL2K에서 Connection Pooling을 기본적으로 지원하기 때문에

Connection에 대해 크게 신경을 안쓰고 작성 했었는데, 이부분이 살짝 걸리긴 했다.

다른 방식에 대해서 성능 테스트를 해보지 못하고,

시간에 쫒겨서 만들었기 때문에  한번 검증해 보고자 테스트 코드를 만들어봤다.



환경은 Windows XP+SP2, MSSQL2005+SP1,

VS2005+SP1, MS OLEDB Provier for SQL Server 이다.

Connection스트링에 대한 옵션은 default값으로 하였다.

4가지 경우에 대한 성능 테스트를 진행해보았다.

공통적인 부분은 스레드 2개를 통해 SP(Stored Procedure)를 호출하는 consumer를 통해

recordset을 fetch하는 부분이다.

* TACCSSOR는 CCommand<CAccessor<CS_SP명> >을 상속한 클래스 인스턴스

 Case1>  Wizard를 통해 생성된 ATL OLEDB 소비자(consumer)를 그대로 사용 했다.
 ThreadFunc1()
 {
  TACCSSOR.OpenAll();  
 ...read...
 TACCSSOR.CloseDataSource();
 }

 Case2> DataSource 연결 1개. DataSource를 통해 Session 연결 1개.
 ThreadFunc2()
 {
 TACCESSOR.Open(미리 생성해둔 Session전달);
 ...read...
 TACCESSOR.Close();
}

 Case3> DataSource 연결 1개.  세션은 매번 생성
 ThreadFunc3()
 {
 CSession  session;
 _session.Open(DataSource); // DataSource를 인자로  Session생성
 TACCESSOR.Open(미리 생성해둔 Session전달);
 ...read...
 TACCESSOR.Close();
}

Case4> DataSource 연결1개, 자체 세션 풀 구성
ThreadFunc4()
{
CSession *pSession = GetSession();
 TACCESSOR.Open(*pSession);
 ...read...
 TACCESSOR.Close();
FreeSession(pSession);
}

즉, 기존에 구성했던 방식은 Case2에 해당된다.

과연 어떤 결과가 나올것인가?? 하하하~ 흥분 흥분 *--*;;

각각의 케이스에 대해 ThreadFunX를 1000회씩 실행한 결과이다.


const int DBPOOL_CASE = 1;   // 9567, 9511, 9560
const int DBPOOL_CASE = 2;   // 4388, 4402, 4375
const int DBPOOL_CASE = 3;   // 1162, 971, 977
const int DBPOOL_CASE = 4;   // 470, 530, 468

왜 이런 결과가 나온것일까??

Case1의 기본으로 생성해주는 consumer코드는 매번 DataSource를 연결하지만

Connection Pool이 구성되어 있다면 Case 2와 크게 차이가 없어야 맞다.

하지만, OpenAll 메소드 안에서 Propertie들에 대해 셋팅하는 부분과 OpenRowset을 호출하는

몇단계의 함수콜이 더 들어가게 된다. 이부분에서 시간을 소요하게 되는것이다.

Case2가 Case3보다 느린이유는 DataSource와 Session을 단일하게 사용하게 되면서

Bottle neck이 발생했다고 보는게 맞다.

(추가적으로 얻을수 있는 정보는 Session이 하나라도 자동적으로 Serialize되기때문에
 
 lock이 필요 없다는것. ^^)

Case3은 세션을 Datasource에서 가져오기 때문에 사용중인 세션에 SP를 호출하는

우를 범하지 않아서 계속 지역변수로써 Session을 생성하지만 더 빠른것이다.



Connection Pool을 직접적으로 체감하기 위해 스레드생성 이전부터 루프를 돌려 테스트를 진행해 보았다.

10회 // 4296, 3285, 1082, 588
20회 // 8801, 5908, 2118, 1089
50회 // 21883, 7544, 3257, 2585

잼있는 결과가 나온다.

자동으로 생성해준 코드가 DataSource와 Session을 미리 연결해놨을때 보다

훨씬 빠른 속도를 보여준다.

왜 이런 결과가 나온것일까?

DataSource와 Session을 단일하게만 사용했기때문에 Pool을 제대로 활용 못한 결과이다.

즉, DataSource와 Session을 매번 생성하는 Case 1의 경우 Pool을 제대로 활용했기때문에

Case1보다 빠른 성능을 보여준다.

하지만 Case3이 가장 빠를수 밖에 없는 이유는 Connection Pool도 활용하면서

선형적인 작업에는 Session Pool이 사용되는 구조이기 때문에 좋은 성능을 보여준다.

Case4는 Session을 미리 생성해두고 사용하기 때문에 지역변수가 Stack쌓이고 정리되는

과정을 줄여 속도를 높이고 있다.



결론적으로, DataSource는 하나로, Session은 필요할때(SP나 Query를 호출할때)

생성해주는게 좋은 성능을 보여준다.

게다가  Session또한 Pool로 구성하면 더욱 좋은 성능을 낼수 있다.



< vector를 이용해 Session Pool을 구현한 급조 소스 >






추가적으로 ...

<< Connection Pool에 대한 기본 개념  >>

Pool?  자료나 공간에 대해 미리 구성을 한뒤에 뽑아서 사용하고 다시 집어넣는 형식을 말함.

Connection Pool? 상호간의 연결에 대한 풀을 구성해놓고 접속을 재사용하는것.

MSSQL에서의 Connection Pool의 특징

1. Database Connection에 대한 풀.
  즉, OLEDB Provider 기준으로 보자면 CDataSrc에 해당한다.

2. 프로세스 단위로 구성
  각 풀은 프로세스 마다 풀을 구성하게 된다.

3. default값으로 Connection pooling 한다.

조건? Connection String부분이 blank하나까지도 동일해야 한다.
        (UDL파일을 사용하여 실수를 줄일수 있다)

,
Technical Article 2007. 9. 26. 20:51

wchar_t 타입을 받는 형식라이브러리 빌드 문제 C2664

#include "stdafx.h"
#import "progid:BWDBAgent.SPMgr.1" no_namespace named_guids

int _tmain(int argc, _TCHAR* argv[])
{
 ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 {
  ISPMgrPtr m_pSpMgr;

  m_pSpMgr = ISPMgrPtr(__uuidof(CSPMgr));

  HRESULT hr = m_pSpMgr->S_AddUser(_T("first1"), 0, _T("what"), _T("whatu"));

 }
 ::CoUninitialize();
 return 0;
}

위와 같은 간단한 코드를 빌드하는데

..\bwdbagenttester\bwdbagenttester.cpp(15) : error C2664: 'ISPMgr::S_AddUser' : 매개 변수 1을(를) 'const wchar_t [7]'에서 'unsigned short *'(으)로 변환할 수 없습니다.
1>        가리킨 형식이 관련이 없습니다. 변환하려면 reinterpret_cast, C 스타일 캐스트 또는 함수 스타일 캐스트가 필요합니다.

이런 에러가 발생하였다.


COM 인터페이스는 다음과 같다.

__interface ISPMgr : IUnknown
{
 [helpstring("메서드 S_AddUser")] HRESULT S_AddUser([in] wchar_t* userid, [in] LONG usertype, [in] wchar_t* password, [in] wchar_t* nick);
};

저번에도 봤었는데 가물가물해서 정리해놓는다.


MSDN에 보면

midl 컴파일러에 있는 버그로 인해 wchar_t 형식이 형식 라이브러리에서 부호 없는 short로 생성됩니다. 이 오류를 해결하려면 C++ 소스 코드에서 형식을 캐스팅하거나 idl 파일에서 형식을 문자열로 정의해야 합니다.




  HRESULT hr = m_pSpMgr->S_AddUser((unsigned short*)("first1"), 0, (unsigned short*)("what"), (unsigned short*)("whatu"));

이렇게 하면 된다.

되도록이면, BSTR을 사용하는게 나을듯 싶다.

최대한 고치지 않고 부분만 빼낼려고 했더니원 -_-;;

,
Technical Article 2007. 7. 24. 10:41

VS2005에서 Wizard를 통해 OLEDB Consumer생성시 Stored Procedure Accessor bind 실패 원인 분석

VS2005에서 Wizard를 통해 OLEDB Consumer생성시 Stored Procedure Accessor bind 실패 원인 분석

Visual Studio 6에서는 Insert - new ATL Object - Data Access - Consumer를 통해
Stored Procedure(이하SP)에 대한 Accessor를 자동으로 생성할수 있다.
이를 통해 다음과 같은 코드를 얻게된다.


// dboSBuddyList1.H : Declaration of the CdboSBuddyList1 class

#ifndef __DBOSBUDDYLIST1_H_
#define __DBOSBUDDYLIST1_H_

class CdboSBuddyList1Accessor
{
public:
 LONG m_RETURNVALUE;
 TCHAR m_userid[13];
 BYTE m_usertype;
 TCHAR m_coluserid[13];
 BYTE m_colusertype;
 TCHAR m_colnick[13];
 BYTE m_collev;
 TCHAR m_colcomment[61];

BEGIN_PARAM_MAP(CdboSBuddyList1Accessor)
 SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
 COLUMN_ENTRY(1, m_RETURNVALUE)
 SET_PARAM_TYPE(DBPARAMIO_INPUT)
 COLUMN_ENTRY(2, m_userid)
 COLUMN_ENTRY(3, m_usertype)
END_PARAM_MAP()

BEGIN_COLUMN_MAP(CdboSBuddyList1Accessor)
 COLUMN_ENTRY(1, m_coluserid)
 COLUMN_ENTRY(2, m_colusertype)
 COLUMN_ENTRY(3, m_colnick)
 COLUMN_ENTRY(4, m_collev)
 COLUMN_ENTRY(5, m_colcomment)
END_COLUMN_MAP()

DEFINE_COMMAND(CdboSBuddyList1Accessor, _T("{ ? = CALL dbo.S_BuddyList;1 (?,?) }"))

 // You may wish to call this function if you are inserting a record and wish to
 // initialize all the fields, if you are not going to explicitly set all of them.
 void ClearRecord()
 {
  memset(this, 0, sizeof(*this));
 }
};

class CdboSBuddyList1 : public CCommand<CAccessor<CdboSBuddyList1Accessor> >
{
public:
 HRESULT Open()
 {
  HRESULT  hr;

  hr = OpenDataSource();
  if (FAILED(hr))
   return hr;

  return OpenRowset();
 }
 HRESULT OpenDataSource()
 {
  HRESULT  hr;
  CDataSource db;
  CDBPropSet dbinit(DBPROPSET_DBINIT);

  dbinit.AddProperty(DBPROP_AUTH_PASSWORD, OLESTR("wing_access"));
  dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("xxxxx"));
  dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("xxxxx"));
  dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("xx.xx.xx.xx"));
  dbinit.AddProperty(DBPROP_INIT_LCID, (long)1042);
  dbinit.AddProperty(DBPROP_INIT_PROMPT, (short)4);
  hr = db.Open(_T("SQLOLEDB.1"), &dbinit);
  if (FAILED(hr))
   return hr;

  return m_session.Open(db);
 }
 HRESULT OpenRowset()
 {
  return CCommand<CAccessor<CdboSBuddyList1Accessor> >::Open(m_session);
 }
 CSession m_session;
};

#endif // __DBOSBUDDYLIST1_H_


이를 사용 하는 방법은

#include "dboSBuddyList1.H"
#define DB_STRING_FIELD_COPY(dest, src)  _tcsncpy(dest, src, sizeof(dest))


void CDBTT2Dlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 
 CdboSBuddyList1 rs;
 HRESULT hr;

 DB_STRING_FIELD_COPY(rs.m_userid,  _T("lovydayz"));
 rs.m_usertype = 0;

 hr = rs.Open();

 hr = rs.MoveFirst();

 rs.Close();

}



이런식이다. 아이디와 타입을 SP의 인자로 넘기고 레코드셋을 받아오는 SP이다.


VS2005에서는 추가-클래스-ATL OLEDB 소비자 를 통해서 생성하는데
SP에 대해서는  insert, delete, update옵션이 적용되지 않으면 Accessor인지, Table인지
설정하는 옵션이 disable되고 파일을 생성해준다.


class CS_BuddyListAccessor
{
public:

 // 다음 마법사 생성 데이터 멤버에는  열 맵의 해당
 // 필드에 대한 상태 값이 들어 있습니다. 이 값을
 // 사용하여 데이터베이스에서반환하는 NULL 값을
 // 보유하거나 컴파일러에서 오류를 반환할 때
 // 오류 정보를 보유할 수 있습니다. 이러한 필드 사용에
 // 대한 자세한 내용은 Visual C++ 설명서의
 //  "마법사 생성 접근자"에서 "필드 상태 데이터 멤버"를 참조하십시오.
 // 참고: 데이터를 설정/삽입하기 전에 이들 필드를 초기화해야 합니다.

 DBSTATUS m_dwuserid1Status;
 DBSTATUS m_dwusertype1Status;
 DBSTATUS m_dwnickStatus;
 DBSTATUS m_dwlevStatus;
 DBSTATUS m_dwcommentStatus;

 // 다음 마법사 생성 데이터 멤버에는 열 맵의 해당 필드에 대한
 // 길이 값이 들어 있습니다.
 // 참고: 가변 길이 열의 경우 데이터를 설정/삽입하기 전에
 //       이러한 필드를 초기화해야 합니다.

 DBLENGTH m_dwuserid1Length;
 DBLENGTH m_dwusertype1Length;
 DBLENGTH m_dwnickLength;
 DBLENGTH m_dwlevLength;
 DBLENGTH m_dwcommentLength;


 TCHAR m_userid1[13];
 BYTE m_usertype1;
 TCHAR m_nick[13];
 BYTE m_lev;
 TCHAR m_comment[61];

 // 인자들은 리턴되는 레코드셋보다 상단에 있어야 한다!!! - 이근호 070724
 LONG m_RETURN_VALUE;
 TCHAR m_userid[13];
 BYTE m_usertype;


 void GetRowsetProperties(CDBPropSet* pPropSet)
 {
  pPropSet->AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
  pPropSet->AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
  pPropSet->AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
 }

 HRESULT OpenDataSource()
 {
  CDataSource _db;
  HRESULT hr;
// #error Security Issue: The connection string may contain a password
// 아래 연결 문자열에 일반 텍스트 암호 및/또는
// 다른 중요한 정보가 포함되어 있을 수 있습니다.
// 보안 관련 문제가 있는지 연결 문자열을 검토한 후에 #error을(를) 제거하십시오.
// 다른 형식으로 암호를 저장하거나 다른 사용자 인증을 사용하십시오.
  hr = _db.OpenFromInitializationString(L"Provider=SQLOLEDB.1;Password=xxxxx;Persist Security Info=True;User ID=xxxxx;Initial Catalog=xxxx;Data Source=xx.xx.xx.xx;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=LEEGUNHO;Use Encryption for Data=False;Tag with column collation when possible=False");
  if (FAILED(hr))
  {
#ifdef _DEBUG
   AtlTraceErrorRecords(hr);
#endif
   return hr;
  }
  return m_session.Open(_db);
 }

 void CloseDataSource()
 {
  m_session.Close();
 }

 operator const CSession&()
 {
  return m_session;
 }

 CSession m_session;

 // 일부 공급자와 관련된 몇몇 문제점을 해결하기 위해 아래 코드에서는
 // 공급자가 보고하는 것과 다른 순서로 열을 바인딩할 수 있습니다.

 BEGIN_COLUMN_MAP(CS_BuddyListAccessor)
  COLUMN_ENTRY_LENGTH_STATUS(1, m_userid1, m_dwuserid1Length, m_dwuserid1Status)
  COLUMN_ENTRY_LENGTH_STATUS(2, m_usertype1, m_dwusertype1Length, m_dwusertype1Status)
  COLUMN_ENTRY_LENGTH_STATUS(3, m_nick, m_dwnickLength, m_dwnickStatus)
  COLUMN_ENTRY_LENGTH_STATUS(4, m_lev, m_dwlevLength, m_dwlevStatus)
  COLUMN_ENTRY_LENGTH_STATUS(5, m_comment, m_dwcommentLength, m_dwcommentStatus)
 END_COLUMN_MAP()

 BEGIN_PARAM_MAP(CS_BuddyListAccessor)
  SET_PARAM_TYPE(DBPARAMIO_OUTPUT)
  COLUMN_ENTRY(1, m_RETURN_VALUE)
  SET_PARAM_TYPE(DBPARAMIO_INPUT)
  COLUMN_ENTRY(2, m_userid)
//  SET_PARAM_TYPE(DBPARAMIO_INPUT)
  COLUMN_ENTRY(3, m_usertype)
 END_PARAM_MAP()

 DEFINE_COMMAND_EX(CS_BuddyListAccessor, L"{ ? = CALL dbo.S_BuddyList;1 (?,?) }")

};

class CS_BuddyList : public CCommand<CAccessor<CS_BuddyListAccessor> >
{
public:
 HRESULT OpenAll()
 {
  HRESULT hr;
  hr = OpenDataSource();
  if (FAILED(hr))
   return hr;
  __if_exists(GetRowsetProperties)
  {
   CDBPropSet propset(DBPROPSET_ROWSET);
   __if_exists(HasBookmark)
   {
    if( HasBookmark() )
     propset.AddProperty(DBPROP_IRowsetLocate, true);
   }
   GetRowsetProperties(&propset);
   return OpenRowset(&propset);
  }
  __if_not_exists(GetRowsetProperties)
  {
   __if_exists(HasBookmark)
   {
    if( HasBookmark() )
    {
     CDBPropSet propset(DBPROPSET_ROWSET);
     propset.AddProperty(DBPROP_IRowsetLocate, true);
     return OpenRowset(&propset);
    }
   }
  }
  return OpenRowset();
 }

 HRESULT OpenRowset(DBPROPSET *pPropSet = NULL)
 {
  HRESULT hr = Open(m_session, NULL, pPropSet);
#ifdef _DEBUG
  if(FAILED(hr))
   AtlTraceErrorRecords(hr);
#endif
  return hr;
 }

 void CloseAll()
 {
  Close();
  ReleaseCommand();
  CloseDataSource();
 }
};


 

이와 같이 생성된 코드를 테스트 해보면 결과값을 얻어오지 못함을 알수 있다.

Bind부분에서 ATLASSERT(m_pAccessor != NULL) 에서 걸린다.


왜 바인딩을 못하는것인가??

Wizard page를 통해 생성한 코드가 제대로 실행이 안된다는것은 말이 안된다는 가정하에
문제에 접근해 보았다. ATL코드를 죽 훑어보며 DynamicAccssor를 사용하니 문제없이 되었다.
그렇다면 문제는 ATL코드 자체에 있거나 생성한 코드에 있다는 2가지 결론에 도달할수 있었다.

VS6에서 생성한 코드와 비교하며 매크로의 차이, 멤버변수의 차이, 메소드의 차이 등에 대해서
비교를 해보았다.
DEFINE_COMMAND가 DEFINE_COMMAND_EX로 변경되고 CALL시에 SP이름뒤에 ;가 붙는다는것 이외에는 별 문제가 없었다. 결국 binding시의 변수 순서에 문제가 있는것으로 생각하고,
몇번의 삽질을 통해 SP의 인자로 들어가는 멤버변수는 결과 레코드셋이 담기는 변수들 위에
위치해야 된다는 결과를 얻게 되었다.

거의 3일동안 삽질하다가 오늘도 안되면 그냥 DynamicAccessor를 이용해서 돌아가려고 했는데,
다행히 문제가 해결됐다 ^^;

,
Technical Article 2007. 5. 12. 00:18

[기술세미나] How to use OLEDB using ATL

invalid-file

ATL을 이용해서 OLEDB를 쉽고 편하게 사용하는 방법에 관한 기술세미나



기술공유에 대한 의지보다 발표 능력 향상과 내가 보유한 기술에 대한 정리의 목적이 더 크다.

나눔으로써 얻게되는게 많다는 말이 와닿는 ... ^^


2002년인가 2003년부터 OLEDB에 관심을 갖기시작해서

인터페이스를 접하고 msdn보면서 하나하나 구축해보다

ATL로 사용하면서 그 편의성을 확실히 깨닿고 나서

시간이 지나서 더 까먹기 전에

기초만이라도 정리해놓자는 생각으로 만든 PPT...


아무래도 중고급 기술은 빠져있어서

많은 사람들에게 도움이 될지는 모르겠지만

어쨌든

OLEDB + ATL의 조합은 환상적 +_+


,
Technical Article/펌 2007. 1. 16. 22:09

OLEDB CMultipleResults memory leak(메모리누수) 발생!!

[테스트 환경]
MSSQL2000에 OLEDB로 ATL을 이용하여 접근
개발환경은 .Net2005, VS6 2가지 버젼을 사용해서 테스트


-- 테스트용 Stored Procedure
CREATE PROCEDURE sp_ResultParamIO

@num int,

@Ret int OUTPUT

AS

BEGIN

  select * from UserInfo where ID = @num

  Set @Ret = 99

END


위와같은 2가지 결과를 가지는 SP(이하 Stored Procedure)에서는

다음과 같이 구성할수 있다.

CCommand<CAccessor<CResultParamIO>, CRowset, CMultipleResults> cmdSP;

즉, 로우셋을 가지고 결과가 여러개로 되기 때문에

Cmd.GetNextResult(NULL, true);

를 통해서 @Ret값을 받을수 있게된다.


근데 문제는 CMultipleResults를 붙이게 되면 cmdSP를 호출할때마다

메모리가 늘어나게 된다.

혹시나 OLEDB의 컨넥션풀이나 메모리풀과 관련되었나 싶어서 물리적메모리와

가상메모리가 다 할때가지 기다려봤는데 줄어들지 않았다 -_-

이와같은 문제를 해결하고자 MS문서를 찾아봤는데 UDL파일이나 컨넥션 스트링을 이용한

leak나는 부분이 patch됐다는 사실을 알고 이를 수정해보았으나

여전히 누수가 존재했다 -_-....


데브피아와 여타 개인사이트들, 뉴스그룹을 통해 찾아본 결과

이는 CMultipleResults의 버그라고 볼수 밖에 없다 -_-;;



누구 아는분 있으면 해결책을 제시해 주시오 ㅡㅠ.....


이때문에 SP를 2개로 구성해서 하나 받고 또받고

이런 더러운 구조로 개발을 종료한다.



[참고자료]

OLE DB ? ATL Consumer Templates and “Memory Leaks” · Thu Mar 25,
http://exold.com/article/ole-db--atl-consumer-templates-and-memory-leaks-

BUG: Memory Leak in ATL OLE DB Consumer Template Functions
http://support.microsoft.com/kb/271926


http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=5204&ref=1760
http://www.google.co.kr/search?sourceid=navclient&hl=ko&ie=UTF-8&rlz=1T4GGLG_ko___KR204&q=msdn+GetNextResult
http://www.google.co.kr/search?hl=ko&newwindow=1&rlz=1T4GGLG_ko___KR204&q=CMultipleResults&lr=
http://dbforums.com/archive/index.php/t-481961.html
http://msdn2.microsoft.com/en-us/library/ms713643.aspx

,
TOTAL TODAY