/*************************************************************************** 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 #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::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(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(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::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::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::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 == CDBConnPool::POOL_FREE) { iter->second = CDBConnPool::POOL_USE; d->PgDoExec( const_cast (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(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::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::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; }