546 lines
10 KiB
C++
546 lines
10 KiB
C++
/***************************************************************************
|
|
Database Connection Pool
|
|
-----------------------------------------
|
|
begin : 2012/05/13
|
|
copyright : (C) 2011 SolutionBox Inc.
|
|
author : Service 1 Team
|
|
email : svc1@solbox.com
|
|
version : 3.1.0
|
|
|
|
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
|
|
Redistribution and use in source and binary forms, with or with out
|
|
modification, are not permitted in outside of SolutionBox Inc.
|
|
***************************************************************************/
|
|
#include "DBConnPool.h"
|
|
#include "Database.h"
|
|
#include "Logger.h"
|
|
#include "Util.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#define GET_SLEEP 1000 // microsecond
|
|
|
|
CMasterDBPool *CMasterDBPool::m_inst = NULL;
|
|
CSlaveDBPool *CSlaveDBPool::m_inst = NULL;
|
|
|
|
CDBConnPool::CDBConnPool(CDataBaseInfo info)
|
|
: m_dbinfo(info), m_exitpool(false), m_lastset(NULL), m_alivetime(-1), m_tabletype(-1)
|
|
{
|
|
pthread_mutex_init(&m_mutex, NULL);
|
|
pthread_mutex_init(&m_alivemutex, NULL);
|
|
pthread_cond_init(&m_alivecond, NULL);
|
|
}
|
|
|
|
CDBConnPool::~CDBConnPool()
|
|
{
|
|
pthread_mutex_destroy(&m_mutex);
|
|
pthread_mutex_destroy(&m_alivemutex);
|
|
pthread_cond_destroy(&m_alivecond);
|
|
}
|
|
|
|
int CDBConnPool::ReCreatePool(int poolcnt /* = 4 */)
|
|
{
|
|
// pool
|
|
for (int i = 0; i < poolcnt; i++)
|
|
{
|
|
DataBase * tmp = new DataBase();
|
|
|
|
if (tmp->PgOpenDB(m_dbinfo.m_hostaddr, m_dbinfo.m_port, m_dbinfo.m_dbname, m_dbinfo.m_user, m_dbinfo.m_pw) == NULL)
|
|
{
|
|
#ifdef _DEBUG
|
|
cerr << "Database connect error : " << tmp->GetErrorMessage() << endl;
|
|
#endif // _DEBUG
|
|
LOG(LERR, "Database connect error.[%s]", tmp->GetErrorMessage().c_str());
|
|
delete tmp;
|
|
continue;
|
|
}
|
|
|
|
// 2014.06.06 dadamin
|
|
// Pool 생성 시 현재 생성된 DB의 형상을 체크한다.
|
|
if (m_tabletype < 0)
|
|
{
|
|
if (SetTargetTableType(tmp) == false)
|
|
{
|
|
delete tmp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_poolmap.insert(make_pair(tmp, CDBConnPool::POOL_FREE));
|
|
}
|
|
|
|
LOG(LDBG, "DB Pool Recreated.[CNT=%zu]", m_poolmap.size());
|
|
return m_poolmap.size();
|
|
}
|
|
|
|
int CDBConnPool::CreatePool( int poolcnt /* = 4 */ )
|
|
{
|
|
// keep alive thread
|
|
int nRet = pthread_create(&m_keepalive, 0, CDBConnPool::KeepPoolAlive, this);
|
|
if( nRet )
|
|
{
|
|
cerr << "Thread create failed.: errno: " << errno << endl;
|
|
return -1;
|
|
}
|
|
|
|
// pool
|
|
m_poolsize = ReCreatePool(poolcnt);
|
|
|
|
if( m_poolmap.size() == 0 )
|
|
{
|
|
// alive thread end
|
|
m_exitpool = true;
|
|
pthread_cond_signal(&m_alivecond);
|
|
pthread_join(m_keepalive, NULL);
|
|
m_exitpool = false;
|
|
return -1;
|
|
}
|
|
|
|
return m_poolsize;
|
|
}
|
|
|
|
int CDBConnPool::DestroyPool()
|
|
{
|
|
m_exitpool = true;
|
|
map<DataBase*, short>::iterator iter;
|
|
pthread_mutex_lock(&m_mutex);
|
|
while(m_poolmap.size() > 0 )
|
|
{
|
|
iter = m_poolmap.begin();
|
|
if(iter->second == CDBConnPool::POOL_FREE || iter->second == CDBConnPool::POOL_ERR)
|
|
{
|
|
DataBase *data = static_cast<DataBase *>(iter->first);
|
|
delete (DataBase *) data;
|
|
m_poolmap.erase(iter);
|
|
}
|
|
else
|
|
sleep(1);
|
|
}
|
|
pthread_mutex_unlock(&m_mutex);
|
|
|
|
//
|
|
pthread_cond_signal(&m_alivecond);
|
|
pthread_join(m_keepalive, NULL);
|
|
|
|
/*
|
|
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
|
|
{
|
|
if(iter->second == CDBConnPool::POOL_FREE)
|
|
{
|
|
DataBase *data = static_cast<DataBase *>(iter->first);
|
|
delete (DataBase *) data;
|
|
m_poolmap.f
|
|
m_poolmap.erase(iter);
|
|
}
|
|
}
|
|
*/
|
|
|
|
m_poolsize = m_poolmap.size();
|
|
return m_poolsize;
|
|
}
|
|
|
|
DataBase * CDBConnPool::GetConnFromPool(int timeout)
|
|
{
|
|
ostringstream msg;
|
|
DataBase * r = NULL;
|
|
|
|
int64_t usetime = 0;
|
|
int64_t out = timeout*1000*1000;
|
|
|
|
time_t t = time(NULL);
|
|
pthread_mutex_lock(&m_mutex);
|
|
map<DataBase*, short>::iterator iter = m_poolmap.begin();
|
|
if(m_lastset)
|
|
{
|
|
iter = m_poolmap.find(m_lastset);
|
|
if( iter != m_poolmap.end() )
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
|
|
if( m_poolmap.empty() )
|
|
{
|
|
if (ReCreatePool(m_poolsize) == 0)
|
|
{
|
|
msg << "DB Pool empty.";
|
|
LOG(LERR, msg.str().c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( iter == m_poolmap.end() )
|
|
{
|
|
iter = m_poolmap.begin();
|
|
}
|
|
|
|
for( ; !m_poolmap.empty()&& iter != m_poolmap.end(); )
|
|
{
|
|
if(iter->second == CDBConnPool::POOL_FREE)
|
|
{
|
|
iter->second = CDBConnPool::POOL_USE;
|
|
r = iter->first;
|
|
m_lastset = r;
|
|
break;
|
|
}
|
|
else if (iter->second == CDBConnPool::POOL_ERR)
|
|
{
|
|
DataBase * d = iter->first;
|
|
m_poolmap.erase(iter++);
|
|
if(d == m_lastset)
|
|
m_lastset = NULL;
|
|
delete d;
|
|
}
|
|
else
|
|
iter++;
|
|
}
|
|
|
|
if( r == NULL)
|
|
{
|
|
solusleep(GET_SLEEP);
|
|
if( usetime > 10*1000*1000)
|
|
{
|
|
msg << "GetConnFromPool have waited longer than 10 seconds.";
|
|
LOG(LDEV1, msg.str().c_str());
|
|
}
|
|
|
|
if( out > 0 )
|
|
{
|
|
usetime += GET_SLEEP;
|
|
|
|
if(usetime > out)
|
|
{
|
|
msg << "GetConnFromPool Timeout." << usetime << "," << out <<
|
|
"," << time(NULL) -t;
|
|
LOG(LERR, msg.str().c_str());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (r == NULL);
|
|
pthread_mutex_unlock(&m_mutex);
|
|
|
|
return r;
|
|
}
|
|
|
|
void CDBConnPool::ReleaseConnToPool( DataBase * t, bool success )
|
|
{
|
|
//pthread_mutex_lock(&m_mutex);
|
|
|
|
map<DataBase*, short>::iterator iter = m_poolmap.find(t);
|
|
if( iter != m_poolmap.end() )
|
|
{
|
|
if(iter->second==CDBConnPool::POOL_USE)
|
|
{
|
|
if (success)
|
|
{
|
|
iter->second=CDBConnPool::POOL_FREE;
|
|
}
|
|
else
|
|
{
|
|
ostringstream msg;
|
|
iter->second=CDBConnPool::POOL_ERR;
|
|
msg << "DB Pool used failed.";
|
|
LOG(LERR, msg.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//pthread_mutex_unlock(&m_mutex);
|
|
}
|
|
|
|
bool CDBConnPool::IsTimeoutAlive()
|
|
{
|
|
bool r = false;
|
|
pthread_mutex_lock(&m_alivemutex);
|
|
struct timespec to;
|
|
|
|
if(m_alivetime > 0 )
|
|
to.tv_sec = time(NULL) + m_alivetime;
|
|
else
|
|
to.tv_sec = time(NULL) + 5;
|
|
|
|
to.tv_nsec = 0;
|
|
|
|
int err = pthread_cond_timedwait(&m_alivecond, &m_alivemutex, &to);
|
|
|
|
if (err == ETIMEDOUT)
|
|
{
|
|
if( m_alivetime > 0 )
|
|
r = true;
|
|
}
|
|
else if ( err == 0 )
|
|
{
|
|
#ifdef _DEBUG
|
|
cout << "Set Keep alive Timeout : " << m_alivetime << endl;
|
|
#endif //_DEBUG
|
|
r = false;
|
|
}
|
|
else
|
|
{
|
|
/* nothing */
|
|
}
|
|
|
|
pthread_mutex_unlock(&m_alivemutex);
|
|
|
|
return r;
|
|
}
|
|
|
|
bool CDBConnPool::SendAliveMsg()
|
|
{
|
|
ostringstream msg;
|
|
ostringstream sql;
|
|
|
|
pthread_mutex_lock(&m_mutex);
|
|
map<DataBase*, short>::iterator iter = m_poolmap.begin();
|
|
map<DataBase*, short>::iterator enditer = m_poolmap.end();
|
|
|
|
sql << "SELECT sp_user_seq, sp_svc_tran_id FROM t_sms_sp_svc_product LIMIT 1";
|
|
while(iter != enditer)
|
|
{
|
|
DataBase * d = iter->first;
|
|
if(iter->second == CDBConnPool::POOL_FREE)
|
|
{
|
|
iter->second = CDBConnPool::POOL_USE;
|
|
d->PgDoExec( const_cast<char*> (sql.str().c_str()) );
|
|
if( d->PgResult(DataBase::CLEAR) < 0 )
|
|
{
|
|
msg << "Send alive : message failed : " << iter->first->GetErrorMessage();
|
|
LOG(LERR, msg.str().c_str());
|
|
iter->second=CDBConnPool::POOL_ERR;
|
|
iter++;
|
|
//m_poolmap.erase(iter++);
|
|
//delete d;
|
|
}
|
|
else
|
|
{
|
|
iter->second = CDBConnPool::POOL_FREE;
|
|
msg << "Send alive : message success.";
|
|
LOG(LDEV1, msg.str().c_str());
|
|
++iter;
|
|
}
|
|
}
|
|
else if (iter->second == CDBConnPool::POOL_ERR)
|
|
{
|
|
msg << "Send alive : Error Pool remove.";
|
|
LOG(LDBG, msg.str().c_str());
|
|
m_poolmap.erase(iter++);
|
|
delete d;
|
|
}
|
|
else
|
|
++iter;
|
|
|
|
}
|
|
pthread_mutex_unlock(&m_mutex);
|
|
return true;
|
|
}
|
|
|
|
void* CDBConnPool::KeepPoolAlive( void* pdata )
|
|
{
|
|
CDBConnPool* pObject = reinterpret_cast<CDBConnPool *>(pdata);
|
|
ostringstream msg;
|
|
|
|
while( pObject->IsExit() == false )
|
|
{
|
|
bool b = pObject->IsTimeoutAlive();
|
|
msg << "KeepPoolAlive Timeout - run : " << b;
|
|
LOG(LDEV1, msg.str().c_str());
|
|
if( pObject->IsExit() == false && b )
|
|
{
|
|
// send keep alive message
|
|
pObject->SendAliveMsg();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CDBConnPool::SetKeepAliveTimeout(int sec)
|
|
{
|
|
m_alivetime = sec;
|
|
pthread_cond_signal(&m_alivecond);
|
|
return m_alivetime;
|
|
}
|
|
|
|
int CDBConnPool::GetFreePoolCnt()
|
|
{
|
|
int r = 0;
|
|
map<DataBase*, short>::iterator iter;
|
|
// pthread_mutex_lock(&m_mutex);
|
|
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
|
|
{
|
|
if(iter->second == CDBConnPool::POOL_FREE)
|
|
{
|
|
r++;
|
|
}
|
|
}
|
|
// pthread_mutex_unlock(&m_mutex);
|
|
return r;
|
|
}
|
|
|
|
// 2014.06.06 dadamin
|
|
// 현재 DB 형상 체크
|
|
bool CDBConnPool::SetTargetTableType(DataBase * d)
|
|
{
|
|
ostringstream msg;
|
|
|
|
if (d == NULL)
|
|
return false;
|
|
|
|
string sql = "SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource'";
|
|
|
|
d->PgDoExec(sql);
|
|
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
|
|
{
|
|
d->PgClear();
|
|
msg << "SetTargetTableType failed : " << d->GetErrorMessage();
|
|
LOG(LERR, msg.str().c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
if (d->GetNoTuples() > 0)
|
|
{
|
|
m_tabletype = 0;
|
|
}
|
|
else
|
|
{
|
|
m_tabletype = 1;
|
|
}
|
|
|
|
d->PgClear();
|
|
msg << "Set Target Table Type : " << m_tabletype;
|
|
|
|
LOG(LDBG, msg.str().c_str());
|
|
return true;
|
|
}
|
|
|
|
// 작업할 메타 테이블명
|
|
string CDBConnPool::GetMetaTableName(const char * tranid)
|
|
{
|
|
string r;
|
|
|
|
switch (m_tabletype)
|
|
{
|
|
case 1:
|
|
r = "t_meta_";
|
|
r += tranid;
|
|
break;
|
|
case 0:
|
|
default:
|
|
r = "t_dav_resource";
|
|
break;
|
|
}
|
|
|
|
LOG(LDBG, "Working Table Name %s", r.c_str());
|
|
|
|
return r;
|
|
}
|
|
|
|
void CDBConnPool::printstatus(string prefixed)
|
|
{
|
|
int f = 0, u = 0;
|
|
map<DataBase*, short>::iterator iter;
|
|
|
|
if(m_exitpool == true ) return;
|
|
// pthread_mutex_lock(&m_mutex);
|
|
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
|
|
{
|
|
if(iter->second == CDBConnPool::POOL_FREE)
|
|
{
|
|
f++;
|
|
}
|
|
else
|
|
{
|
|
u++;
|
|
}
|
|
}
|
|
|
|
ostringstream msg;
|
|
if( prefixed.empty() == false )
|
|
msg << "["<< prefixed <<"]";
|
|
|
|
msg <<"DB connection Pool - " <<"total : " << m_poolmap.size() << "(" << u <<
|
|
"/" << f << ")";
|
|
|
|
LOG(LINF, msg.str().c_str());
|
|
}
|
|
|
|
// master db pool
|
|
CMasterDBPool::CMasterDBPool(CDataBaseInfo info)
|
|
: CDBConnPool(info)
|
|
{
|
|
}
|
|
|
|
CMasterDBPool::~CMasterDBPool()
|
|
{
|
|
release();
|
|
}
|
|
|
|
void CMasterDBPool::init(CDataBaseInfo info)
|
|
{
|
|
if( CMasterDBPool::m_inst == NULL )
|
|
{
|
|
CMasterDBPool::m_inst = new CMasterDBPool(info);
|
|
}
|
|
}
|
|
|
|
CMasterDBPool* CMasterDBPool::getInstance()
|
|
{
|
|
return CMasterDBPool::m_inst;
|
|
}
|
|
|
|
int CMasterDBPool::release()
|
|
{
|
|
int r = -1;
|
|
if( CMasterDBPool::m_inst != NULL )
|
|
{
|
|
r = CMasterDBPool::m_inst->DestroyPool();
|
|
delete CMasterDBPool::m_inst;
|
|
CMasterDBPool::m_inst = NULL;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
// slave db pool
|
|
CSlaveDBPool::CSlaveDBPool(CDataBaseInfo info)
|
|
: CDBConnPool(info)
|
|
{
|
|
}
|
|
|
|
CSlaveDBPool::~CSlaveDBPool()
|
|
{
|
|
release();
|
|
}
|
|
|
|
void CSlaveDBPool::init(CDataBaseInfo info)
|
|
{
|
|
if( CSlaveDBPool::m_inst == NULL )
|
|
{
|
|
CSlaveDBPool::m_inst = new CSlaveDBPool(info);
|
|
}
|
|
}
|
|
|
|
CSlaveDBPool* CSlaveDBPool::getInstance()
|
|
{
|
|
return CSlaveDBPool::m_inst;
|
|
}
|
|
|
|
int CSlaveDBPool::release()
|
|
{
|
|
int r = -1;
|
|
if(CSlaveDBPool::m_inst != NULL )
|
|
{
|
|
r = CSlaveDBPool::m_inst->DestroyPool();
|
|
delete CSlaveDBPool::m_inst;
|
|
CSlaveDBPool::m_inst = NULL;
|
|
}
|
|
|
|
return r;
|
|
}
|