void ServerPool::LogDebugInfo() { info(" ---------- ServerPool"); info(" Max-Level: %i", m_iMaxNormLevel); m_mutexConnections.Lock(); time_t tCurTime = time(NULL); info(" Servers: %i", m_Servers.size()); for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++) { NewsServer* pNewsServer = *it; info(" %i) %s (%s): Level=%i, NormLevel=%i, BlockSec=%i", pNewsServer->GetID(), pNewsServer->GetName(), pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel(), pNewsServer->GetBlockTime() && pNewsServer->GetBlockTime() + m_iRetryInterval > tCurTime ? pNewsServer->GetBlockTime() + m_iRetryInterval - tCurTime : 0); } info(" Levels: %i", m_Levels.size()); int index = 0; for (Levels::iterator it = m_Levels.begin(); it != m_Levels.end(); it++, index++) { int iSize = *it; info(" %i: Free connections=%i", index, iSize); } info(" Connections: %i", m_Connections.size()); for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++) { PooledConnection* pConnection = *it; info(" %i) %s (%s): Level=%i, NormLevel=%i, InUse:%i", pConnection->GetNewsServer()->GetID(), pConnection->GetNewsServer()->GetName(), pConnection->GetNewsServer()->GetHost(), pConnection->GetNewsServer()->GetLevel(), pConnection->GetNewsServer()->GetNormLevel(), (int)pConnection->GetInUse()); } m_mutexConnections.Unlock(); }
void ServerPool::InitConnections() { debug("Initializing connections in ServerPool"); m_mutexConnections.Lock(); NormalizeLevels(); m_Levels.clear(); for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++) { NewsServer* pNewsServer = *it; pNewsServer->SetBlockTime(0); int iNormLevel = pNewsServer->GetNormLevel(); if (pNewsServer->GetNormLevel() > -1) { if ((int)m_Levels.size() <= iNormLevel) { m_Levels.push_back(0); } if (pNewsServer->GetActive()) { int iConnections = 0; for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++) { PooledConnection* pConnection = *it; if (pConnection->GetNewsServer() == pNewsServer) { iConnections++; } } for (int i = iConnections; i < pNewsServer->GetMaxConnections(); i++) { PooledConnection* pConnection = new PooledConnection(pNewsServer); pConnection->SetTimeout(m_iTimeout); m_Connections.push_back(pConnection); iConnections++; } m_Levels[iNormLevel] += iConnections; } } } m_iGeneration++; m_mutexConnections.Unlock(); }
void ServerPool::CloseUnusedConnections() { m_mutexConnections.Lock(); time_t curtime = ::time(NULL); // close and free all connections of servers which were disabled since the last check int i = 0; for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); ) { PooledConnection* pConnection = *it; bool bDeleted = false; if (!pConnection->GetInUse() && (pConnection->GetNewsServer()->GetNormLevel() == -1 || !pConnection->GetNewsServer()->GetActive())) { debug("Closing (and deleting) unused connection to server%i", pConnection->GetNewsServer()->GetID()); if (pConnection->GetStatus() == Connection::csConnected) { pConnection->Disconnect(); } delete pConnection; m_Connections.erase(it); it = m_Connections.begin() + i; bDeleted = true; } if (!bDeleted) { it++; i++; } } // close all opened connections on levels not having any in-use connections for (int iLevel = 0; iLevel <= m_iMaxNormLevel; iLevel++) { // check if we have in-use connections on the level bool bHasInUseConnections = false; int iInactiveTime = 0; for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++) { PooledConnection* pConnection = *it; if (pConnection->GetNewsServer()->GetNormLevel() == iLevel) { if (pConnection->GetInUse()) { bHasInUseConnections = true; break; } else { int tdiff = (int)(curtime - pConnection->GetFreeTime()); if (tdiff > iInactiveTime) { iInactiveTime = tdiff; } } } } // if there are no in-use connections on the level and the hold time out has // expired - close all connections of the level. if (!bHasInUseConnections && iInactiveTime > CONNECTION_HOLD_SECODNS) { for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++) { PooledConnection* pConnection = *it; if (pConnection->GetNewsServer()->GetNormLevel() == iLevel && pConnection->GetStatus() == Connection::csConnected) { debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID()); pConnection->Disconnect(); } } } } m_mutexConnections.Unlock(); }
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers) { PooledConnection* pConnection = NULL; m_mutexConnections.Lock(); time_t tCurTime = time(NULL); if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0) { Connections candidates; candidates.reserve(m_Connections.size()); for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++) { PooledConnection* pCandidateConnection = *it; NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer(); if (!pCandidateConnection->GetInUse() && pCandidateServer->GetActive() && pCandidateServer->GetNormLevel() == iLevel && (!pWantServer || pCandidateServer == pWantServer || (pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())) && (pCandidateConnection->GetStatus() == Connection::csConnected || !pCandidateServer->GetBlockTime() || pCandidateServer->GetBlockTime() + m_iRetryInterval <= tCurTime || pCandidateServer->GetBlockTime() > tCurTime)) { // free connection found, check if it's not from the server which should be ignored bool bUseConnection = true; if (pIgnoreServers && !pWantServer) { for (Servers::iterator it = pIgnoreServers->begin(); it != pIgnoreServers->end(); it++) { NewsServer* pIgnoreServer = *it; if (pIgnoreServer == pCandidateServer || (pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() && pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel())) { bUseConnection = false; break; } } } pCandidateServer->SetBlockTime(0); if (bUseConnection) { candidates.push_back(pCandidateConnection); } } } if (!candidates.empty()) { // Peeking a random free connection. This is better than taking the first // available connection because provides better distribution across news servers, // especially when one of servers becomes unavailable or doesn't have requested articles. int iRandomIndex = rand() % candidates.size(); pConnection = candidates[iRandomIndex]; pConnection->SetInUse(true); } if (pConnection) { m_Levels[iLevel]--; } } m_mutexConnections.Unlock(); return pConnection; }