/*************************************************************************** Database Connection Pool ( DbConnPool.cpp ) ----------------------------------------- begin : 2013/02/26 copyright : (C) 2013 Solbox Inc. author : Development 1 Team - 2013/02/26 - 1st dadamin email : dev1@solbox.com version : 3.2.0 CopyRight(C) 2005 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 "rc_mngd.h" #include "Configs.h" #include "DbConnPool.h" #include "Database.h" #include "Logger.h" #include "UtilFn.h" #include #define GET_SLEEP 1000 // microsecond CMasterDBpool *CMasterDBpool::m_inst = NULL; CSlaveDBpool *CSlaveDBpool::m_inst = NULL; CConnDBpool::CConnDBpool(CDatabaseInfo info) : m_dbinfo(info), m_exitpool(false), m_lastset(NULL) ,m_poolcnt(0), m_isrecreate(false), m_alivetime(-1) ,m_tabletype(-1) { pthread_mutex_init(&m_mutex, NULL); pthread_mutex_init(&m_alivemutex, NULL); pthread_cond_init(&m_alivecond, NULL); } CConnDBpool::~CConnDBpool() { pthread_mutex_destroy(&m_mutex); pthread_mutex_destroy(&m_alivemutex); pthread_cond_destroy(&m_alivecond); } int CConnDBpool::MakeConnect( int pool ) { ostringstream msg; for(int i=0 ; i < pool ; i++) { DataBase * tmp = new DataBase(); if( tmp->PgOpenDB( m_dbinfo.m_szRcdbIp, m_dbinfo.m_nRcdbPort, m_dbinfo.m_szRcdbName, m_dbinfo.m_szRcdbAcct, m_dbinfo.m_szRcdbAcctPw ) == NULL ) { msg << "Database connect error : " << tmp->GetErrorMessage(); LOGACONSOLE(LERR, msg); delete tmp; continue; } // XML library(UTF-8 ÀÚµ¿ º¯È¯) ÀÎÄÚµù ¹®ÀÚ¸¦ Áö¿øÇϱâ À§Çؼ­ DB client UTF-8 ¼³Á¤ÇÔ if (tmp->PgSetClientEncoding("UTF-8") != 0) { msg << "Database Set Client Encoding error : " << tmp->GetErrorMessage(); LOGACONSOLE(LERR, msg); delete tmp; continue; } // 2014.06.06 dadamin // Pool »ý¼º ½Ã ÇöÀç »ý¼ºµÈ DBÀÇ Çü»óÀ» üũÇÑ´Ù. if (m_tabletype < 0) { if (SetTargetTableType(tmp) == false) { delete tmp; DestroyPool(); break; } } m_poolmap.insert(make_pair(tmp, CConnDBpool::POOL_FREE)); } return m_poolmap.size(); } int CConnDBpool::CreatePool( int poolcnt /* = 4 */ ) { ostringstream msg; // keep alive thread int nRet = pthread_create(&m_keepalive, 0, CConnDBpool::KeepPoolAlive, this); if( nRet ) { msg << "Thread create failed.: errno: " << errno; LOGACONSOLE(LERR, msg); return -1; } // pool m_poolcnt = MakeConnect(poolcnt); if (m_poolcnt == 0) { // alive thread end m_exitpool = true; pthread_cond_signal(&m_alivecond); pthread_join(m_keepalive, NULL); m_exitpool = false; return -1; } return m_poolcnt; } int CConnDBpool::DestroyPool() { m_exitpool = true; map::iterator iter; pthread_mutex_lock(&m_mutex); while(m_poolmap.size() > 0 ) { iter = m_poolmap.begin(); if(iter->second == CConnDBpool::POOL_FREE || iter->second == CConnDBpool::POOL_ERR || iter->second == CConnDBpool::POOL_CLOSE ) { DataBase *data = static_cast(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 == conndbpool::POOL_FREE) { DataBase *data = static_cast(iter->first); delete (DataBase *) data; m_poolmap.f m_poolmap.erase(iter); } } */ return m_poolmap.size(); } DataBase * CConnDBpool::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::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() ) { int make = 0; if(m_isrecreate) { #ifdef _DEBUG msg << "DB Pool ReCeate."; LOGACONSOLE(LDBG, msg); #endif // _DEBUG make = MakeConnect(m_poolcnt); } if (make == 0) { msg << "DB Pool empty."; LOGACONSOLE(LERR, msg); break; } } if( iter == m_poolmap.end() ) { iter = m_poolmap.begin(); } for( ; !m_poolmap.empty()&& iter != m_poolmap.end(); ) { if(iter->second == CConnDBpool::POOL_FREE) { iter->second = CConnDBpool::POOL_USE; r = iter->first; m_lastset = r; break; } else if (iter->second == CConnDBpool::POOL_ERR || iter->second == CConnDBpool::POOL_CLOSE) { bool is_closed = (iter->second == CConnDBpool::POOL_CLOSE); DataBase * d = iter->first; if(is_closed) { msg << "Force Closed. And Add Database object."; LOGACONSOLE(LDBG, msg); MakeConnect(1); } 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) { #ifdef _DEBUG msg << "GetConnFromPool have waited longer than 10 seconds."; LOGACONSOLE(LDBG, msg); #endif // _DEBUG } if( out > 0 ) { usetime += GET_SLEEP; if(usetime > out) { msg << "GetConnFromPool Timeout." << usetime << "," << out << "," << time(NULL) -t; LOGACONSOLE(LWAR, msg); break; } } } } while (r == NULL); pthread_mutex_unlock(&m_mutex); return r; } void CConnDBpool::ReleaseConnToPool(DataBase * t, short nMode /* = CConnDBpool::POOL_USE */) { //pthread_mutex_lock(&m_mutex); map::iterator iter = m_poolmap.find(t); if( iter != m_poolmap.end() ) { if(iter->second==CConnDBpool::POOL_USE) { if( nMode == CConnDBpool::POOL_USE) { iter->second = CConnDBpool::POOL_FREE; } else { iter->second = nMode; } } } //pthread_mutex_unlock(&m_mutex); } bool CConnDBpool::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 CConnDBpool::SendAliveMsg() { ostringstream msg; ostringstream sql; pthread_mutex_lock(&m_mutex); map::iterator iter = m_poolmap.begin(); map::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 == CConnDBpool::POOL_FREE) { iter->second = CConnDBpool::POOL_USE; d->PgDoExec( const_cast (sql.str().c_str()) ); if( d->PgResult(DataBase::CLEAR) < 0 ) { msg << "Send alive : message failed : " << iter->first->GetErrorMessage(); LOGACONSOLE(LERR, msg); iter->second=CConnDBpool::POOL_ERR; iter++; //m_poolmap.erase(iter++); //delete d; } else { iter->second = CConnDBpool::POOL_FREE; msg << "Send alive : message success."; LOGACONSOLE(LDBG, msg); ++iter; } } else if (iter->second == CConnDBpool::POOL_ERR) { msg << "Send alive : Error Pool remove."; LOGACONSOLE(LINF, msg); m_poolmap.erase(iter++); delete d; } else ++iter; } if( m_poolmap.empty() ) { int make = 0; if(m_isrecreate) { #ifdef _DEBUG msg << "DB Pool ReCeate."; LOGACONSOLE(LDBG, msg); #endif // _DEBUG make = MakeConnect(m_poolcnt); } if (make == 0) { msg << "DB Pool empty."; LOGACONSOLE(LERR, msg); } } pthread_mutex_unlock(&m_mutex); return true; } void* CConnDBpool::KeepPoolAlive( void* pdata ) { CConnDBpool* pObject = reinterpret_cast(pdata); ostringstream msg; while( pObject->IsExit() == false ) { bool b = pObject->IsTimeoutAlive(); msg << "KeepPoolAlive Timeout - run : " << b; LOGACONSOLE(LDBG, msg); if( pObject->IsExit() == false && b ) { // send keep alive message pObject->SendAliveMsg(); } } return 0; } int CConnDBpool::SetKeepAliveTimeout(int sec) { m_alivetime = sec; pthread_cond_signal(&m_alivecond); return m_alivetime; } int CConnDBpool::GetFreePoolCnt() { int r = 0; map::iterator iter; // pthread_mutex_lock(&m_mutex); for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ ) { if(iter->second == CConnDBpool::POOL_FREE) { r++; } } // pthread_mutex_unlock(&m_mutex); return r; } void CConnDBpool::printstatus(string prefixed) { int f = 0, u = 0; map::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 == CConnDBpool::POOL_FREE) { f++; } else { u++; } } ostringstream msg; if( prefixed.empty() == false ) msg << "["<< prefixed <<"]"; msg <<"DB connection Pool - " <<"total : " << m_poolmap.size() << "(" << u << "/" << f << ")"; LOGACONSOLE(LINF, msg); } // 2014.06.07 dadamin // ÇöÀç DB Çü»ó üũ bool CConnDBpool::SetTargetTableType(DataBase * d) { ostringstream msg; if (d == NULL) return false; d->PgDoExec("SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource'"); if (d->PgResult(DataBase::NOT_CLEAR) < 0) { d->PgClear(); msg << "Set Target Table Type failed : " << d->GetErrorMessage(); LOGACONSOLE(LERR, msg); 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; } // master db pool CMasterDBpool::CMasterDBpool(CDatabaseInfo info) : CConnDBpool(info) { } CMasterDBpool::~CMasterDBpool() { } 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) : CConnDBpool(info) { } CSlaveDBpool::~CSlaveDBpool() { } 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; }