base
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
bool IsRunRcMonD()
|
||||
{
|
||||
char tempBuffer[64];
|
||||
FILE * fd = NULL;
|
||||
bool bRun = false;
|
||||
|
||||
// rc_mond 의 worker 가 동작 중인지 확인을 위해 -n 옵션 사용
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -nx %s | sort", PROCESS_NAME_RC_MONITORING_DAEMON );
|
||||
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "%s process check fail.[%d][%s]", PROCESS_NAME_RC_MONITORING_DAEMON, errorNum, strerror( errorNum ) );
|
||||
|
||||
// 오류 발생시 false 반환하여.. 해당 프로세스가 동작 중이지 않는 것으로 처리.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
if( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
// 프로세스 ID 가 존재하는 경우...
|
||||
//_LOG( LINF, "[SELECT ] %s process found. pid [%d]", PROCESS_NAME_MONITORING_DAEMON, atoi( tempBuffer ) );
|
||||
bRun = true;
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
rc_balanced common header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/26
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __REBALANCE_COMMON_H__
|
||||
#define __REBALANCE_COMMON_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// rc_mond 동작 여부 check 를 위한 프로세스 name
|
||||
#define PROCESS_NAME_RC_MONITORING_DAEMON "rc_mond"
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// rc_mond 가 기동 상태인지 check 를 위한 함수
|
||||
bool IsRunRcMonD();
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __REBALANCE_COMMON_H__ */
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "CopyThread.h"
|
||||
#include "ReportLog.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 5
|
||||
#define SLEEP_TIME_MAX 10
|
||||
|
||||
|
||||
// 본 값은 Makefile 에서 설정
|
||||
//#define MAX_COPY_THREAD_COUNT 2
|
||||
|
||||
|
||||
CCopyControlThread::CCopyControlThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CShmInfoThread * const pShmInfoThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby )
|
||||
, m_pQueueUpdateStandby( pQueueUpdateStandby )
|
||||
, m_pShmInfoThread( pShmInfoThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// Copy Thread count 관련 변수 및 lock 객체 초기화
|
||||
m_nCurrentCopyThreadCount = 0;
|
||||
pthread_mutex_init( &m_mutexCopyThreadCount, NULL );
|
||||
|
||||
m_uTargetFhsSelectFailCount = 0;
|
||||
}
|
||||
|
||||
|
||||
CCopyControlThread::~CCopyControlThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
pthread_mutex_destroy( &m_mutexCopyThreadCount );
|
||||
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pShmInfoThread != NULL )
|
||||
m_pShmInfoThread = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CCopyControlThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL || m_pQueueUpdateStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CCopyControlThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CCopyControlThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CCopyControlThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCopyControlThread::PlusCopyThreadCount()
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
++m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CCopyControlThread::MinusCopyThreadCount()
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
--m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int CCopyControlThread::GetCopyThreadCount()
|
||||
{
|
||||
return m_nCurrentCopyThreadCount;
|
||||
}
|
||||
|
||||
int CCopyControlThread::GetMaxCopyThreadCount()
|
||||
{
|
||||
return MAX_COPY_THREAD_COUNT;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CCopyControlThread::threadFunc( void * arg )
|
||||
{
|
||||
CCopyControlThread * pObject = reinterpret_cast<CCopyControlThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CCopyControlThread::Execute()
|
||||
{
|
||||
// sleep time 조정 처리
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// COPY 처리할 대상 content 없다면...
|
||||
if( m_pQueueMoveStandby->GetSize() == 0 )
|
||||
{
|
||||
// MAX sleep 처리
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[COPY ] standby queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RCDB update standby queue 에 여유 공간이 있는지 check
|
||||
// - 없는 경우 RCDB 장애 등의 상황이므로... MAX sleep 처리한다.
|
||||
unsigned int uUpdatePushEnableCount = m_pQueueUpdateStandby->GetMaxSize() - m_pQueueUpdateStandby->GetSize();
|
||||
|
||||
// RCDB update standby queue 에 10% 정도 여유 공간이 없으면...
|
||||
if( uUpdatePushEnableCount < (m_pQueueUpdateStandby->GetMaxSize() /10) )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[COPY ] rcdb update queue not enough (%u %u). max sleep %u sec."
|
||||
, uUpdatePushEnableCount, m_pQueueUpdateStandby->GetMaxSize() / 10, m_uSleep );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 생성 가능한 copy thread 가 존재하는지 확인
|
||||
unsigned int uEnableThreadCount = GetCopyThreadEnableCount();
|
||||
|
||||
// 생성 가능한 copy therad 가 없는 경우... 잠시 대기 후 다시 시도
|
||||
if( uEnableThreadCount == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
_LOG( LDBG, "[COPY ] copy thread all working [%u / %u]. sleep %u sec."
|
||||
, uEnableThreadCount, MAX_COPY_THREAD_COUNT, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// ShmInfoThread 객체에서 COPY 가능 FHS 정보가 유효한지 check
|
||||
// - COPY 가능 FHS 가 없는 경우.... 잠시 대기후 다시 시도
|
||||
if( m_pShmInfoThread->IsValidCopyTargetFhs() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
LOG( LERR, "CopyControlThread: valid copy target FHS not exist. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
|
||||
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
|
||||
{
|
||||
// 연결 실패시...
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
LOG( LERR, "CopyControlThread: RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// move standby queue 에서 생성 가능한 copy theread 갯수만큼 pop 처리한다.
|
||||
CRebalanceData data;
|
||||
std::list<CRebalanceData> listWork;
|
||||
|
||||
while( uEnableThreadCount > listWork.size() && m_pQueueMoveStandby->GetSize() > 0 )
|
||||
{
|
||||
// Move 대기 queue 에서 data 가져와서 pop 처리하고
|
||||
data = m_pQueueMoveStandby->Front();
|
||||
m_pQueueMoveStandby->Pop();
|
||||
|
||||
// 작업 list 에 등록
|
||||
listWork.push_back( data );
|
||||
}
|
||||
|
||||
_LOG( LINF, "[COPY ] start." );
|
||||
|
||||
// list 에 등록된 각 content 에 대해 해당 content 를 소유한 FHS 정보를 조회한 후 이를 처리할 copy thread 를 생성 처리한다.
|
||||
int nResult = 0;
|
||||
std::list<std::string> listIgnoreFHS;
|
||||
|
||||
while( listWork.empty() == false )
|
||||
{
|
||||
data = listWork.front();
|
||||
listWork.pop_front();
|
||||
|
||||
// RCDB 조회를 통하여 FHS 정보 조회
|
||||
listIgnoreFHS.clear();
|
||||
if( GetMetaHostList( data, rcdb, listIgnoreFHS ) == false )
|
||||
{
|
||||
// meta 정보 조회 실패 또는 해당 content 가 삭제된 상황인 경우...
|
||||
// 해당 함수에서 로깅 했으므로... flow 만 처리
|
||||
// 다시 대기 queue 에 넣을 수 없으므로 그냥 버린다.
|
||||
LOG( LERR, "[COPY ] ignore fhs check fail. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// COPY 처리할 FHS 선정
|
||||
std::string szTargetHost;
|
||||
|
||||
nResult = m_pShmInfoThread->GetCopyTargetFhs( szTargetHost, listIgnoreFHS );
|
||||
if( nResult == 1 ) // COPY 대상 FHS 가 선정된 경우
|
||||
{
|
||||
data.m_szMoveHost = szTargetHost;
|
||||
}
|
||||
else if( nResult == -1 ) // 복사 가능 물리적 FHS 가 없는 경우
|
||||
{
|
||||
// 다시 대기 queue 에 넣을 수 없으므로. 버린다.
|
||||
LOG( LWAR, "[COPY ] proper copy target FHS not exist. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
// 다음 loop 는 좀 더 지연 후 동작하도록 한다.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 복사 가능 물리 FHS 는 존재하지만 ignore 에 포함되어 실제로는 가능 FHS 가 없는 경우
|
||||
// - 다시 대기 queue 에 넣을 수 없으므로. 버린다.
|
||||
LOG( LWAR, "[COPY ] available copy target FHS not exist by ignore list. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패가 지정된 횟수를 초과되는 경우..
|
||||
// - content select mode (ContentSelectMode) 를 big size 순서-> RANDOM 모드로 변경 처리 코드 추가
|
||||
|
||||
// Copy 대상 FHS 선택관련 ignore list 에 의한 실패 count 증가 처리
|
||||
m_uTargetFhsSelectFailCount++;
|
||||
|
||||
// 만약 실패 값이 지정된 값 이상인 경우 .. content SELECT mode 를 random mode 로 변경 처리한다.
|
||||
if( m_uTargetFhsSelectFailCount > TARGET_FHS_SELECT_MAX_FAIL )
|
||||
{
|
||||
if( CProcessConfig::GetInstance()->GetContentSelectMode() != SELECT_MODE_RANDOM )
|
||||
{
|
||||
CProcessConfig::GetInstance()->SetContentSelectMode( SELECT_MODE_RANDOM );
|
||||
|
||||
LOG( LWAR, "[COPY ] content select mode change to RANDOM by copy faiil count over [%u/%d]"
|
||||
, m_uTargetFhsSelectFailCount, TARGET_FHS_SELECT_MAX_FAIL );
|
||||
}
|
||||
|
||||
// 실패 횟수 count 변수 초기화
|
||||
m_uTargetFhsSelectFailCount = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Copy 처리를 수행할 Thread 생성 처리
|
||||
CCopyThread * pCopyThread = new CCopyThread( data, m_pQueueUpdateStandby, this );
|
||||
|
||||
// Thread 생성 또는 start 처리 실패시
|
||||
// - thread 생성이 실패할 가능성은 거의 없고..
|
||||
// - 아마 CopyThread 객체의 Start 처리시 인자 check 등에서 오류일 가능성이 높으므로..
|
||||
// - 재시도 하지 않고.. 그냥 skip 처리한다.
|
||||
if( pCopyThread == NULL || pCopyThread->Start() == false )
|
||||
{
|
||||
// thread 정리
|
||||
if( pCopyThread != NULL )
|
||||
{
|
||||
delete pCopyThread;
|
||||
pCopyThread = NULL;
|
||||
}
|
||||
|
||||
// 실패 내역 로깅
|
||||
LOG( LWAR, "[COPY ] copy thread create or start fail. fhs[%s] svc[%s] resouce[%llu]."
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId );
|
||||
}
|
||||
}
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned int CCopyControlThread::GetCopyThreadEnableCount( )
|
||||
{
|
||||
unsigned int uEnableCount = 0;
|
||||
|
||||
// lock 설정 후 추가 생성 가능한 Copy thread 계산한다.
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
uEnableCount = MAX_COPY_THREAD_COUNT - m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
// 값 보정
|
||||
if( uEnableCount < 0 )
|
||||
uEnableCount = 0;
|
||||
|
||||
return uEnableCount;
|
||||
}
|
||||
|
||||
|
||||
bool CCopyControlThread::GetMetaHostList( CRebalanceData & data, DataBase & rcdb, std::list<std::string> & listHost )
|
||||
{
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 해당 content 를 가진 모든 장비를 조회
|
||||
// - 원본, 복제본은 상관 없으며... cache 는 같은 장비에 있어도 되지만.. 혹시나 해서 제외 목록에 포함
|
||||
// - 삭제된 content 역시 rc_gcmd 가 작업 중 삭제시킬 수 있으므로.. 제외.
|
||||
// - uri 정보는 query 에 적합하게 변환해서 query 수행.
|
||||
// - 원본에 정상적으로 동작하는지 check 한 후 존재할 경우.. 장비 목록을 추출.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT host_name FROM t_meta_%s WHERE uri = "
|
||||
"( SELECT uri FROM t_meta_%s WHERE uri='%s' AND deleted_yn = 'N' AND is_cache = 'N' "
|
||||
" AND resource_type = 0 AND get_content_length != 0); "
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.GetQueryURI().c_str() );
|
||||
|
||||
rcdb.PgDoExec( szQuery );
|
||||
|
||||
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// Query 수행 실패시
|
||||
LOG( LERR, "[COPY ] ignore fhs meta select error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 중복 URI 가 존재하는 경우...
|
||||
// nResult = -4, Error message [ERROR: more than one row returned by a subquery used as an expression] 발생
|
||||
// 해당 상황인 경우.. 운영자가 조치할 수 있도록 report 로깅 처리
|
||||
if( nResult == -4
|
||||
&& strncmp( rcdb.GetErrorMessage().c_str(), "ERROR: more than one row returned", 34) == 0 )
|
||||
{
|
||||
CReportLog reportLog;
|
||||
std::string strErrorMessage;
|
||||
strErrorMessage.clear();
|
||||
|
||||
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
|
||||
{
|
||||
reportLog.GetLastError( strErrorMessage );
|
||||
LOG( LERR, "[COPY ] report log init failed.[%s]", strErrorMessage.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
reportLog.Write( "ERR", "[%s] [duplicate content. please check and delete. host:%s svc:%s id:%llu uri:'%s' ]"
|
||||
, PROG_NAME
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId
|
||||
, data.GetQueryURI().c_str() );
|
||||
|
||||
LOG( LWAR, "[COPY ] duplicate content found. [%s svc:%s id:%llu uri:%s][%s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId
|
||||
, data.GetQueryURI().c_str(), rcdb.GetErrorMessage().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// 오류 발생시
|
||||
LOG( LERR, "[COPY ] copy meta ignore fhs select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 조회 결과가 없는 경우.
|
||||
// Copy 대상 content 가 삭제된 경우....
|
||||
LOG( LERR, "[COPY ] copy meta ignore fhs select not found. [%d][%s]", nResult, szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 조회 결과가 존재.
|
||||
std::string szTemp;
|
||||
listHost.clear();
|
||||
|
||||
// 조회 목록을 list add.
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
szTemp = rcdb.GetValue( i, 0 );
|
||||
listHost.push_back( szTemp );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
|
||||
// 처리 결과 로깅
|
||||
_LOG( LDBG, "[COPY ] copy meta ignore FHS select ok. [%d][%s]", nResult, szQuery );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content copy control thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __COPY_CONTROL_THREAD_H__
|
||||
#define __COPY_CONTROL_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
#include "ShmInfoThread.h"
|
||||
#include "Database.h"
|
||||
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패가 본 횟수를 초과되는 경우..
|
||||
// - content select mode (ContentSelectMode) 를 big size 순서-> RANDOM 모드로 변경 처리
|
||||
#define TARGET_FHS_SELECT_MAX_FAIL 5
|
||||
|
||||
|
||||
// CCopyControlThread class
|
||||
// - Move 대기 queue 에 들어 있는 content 를 pop 하여...
|
||||
// - Move target FHS 를 선택한 후...
|
||||
// - Target FHS 로 content copy 처리를 수행하고...
|
||||
// - 최종 해당 Content 를 RCDB update 처리를 위한 대기 queue 에 push 처리를 수행
|
||||
|
||||
class CCopyControlThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CCopyControlThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CShmInfoThread * const pShmInfoThread
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CCopyControlThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 1 증가시킨다. (Copy Thread 접근 허용을 위해 public 처리)
|
||||
void PlusCopyThreadCount( void );
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 1 감소시킨다. (Copy Thread 접근 허용을 위해 public 처리)
|
||||
void MinusCopyThreadCount( void );
|
||||
|
||||
// Copy 처리 thread 의 현재 count 값을 반환한다.. ( no lock)
|
||||
int GetCopyThreadCount( void );
|
||||
|
||||
// Copy 처리 thread 의 MAX 제한값을 반환한다.. ( no lock)
|
||||
int GetMaxCopyThreadCount( void );
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
|
||||
// Move 처리할 대상을 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// Sharde memory 정보를 추출하여 관리하는 Thread 객체에 대한 포인터
|
||||
CShmInfoThread * m_pShmInfoThread;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 저장하기 위한 변수 및 lock 객체
|
||||
int m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_t m_mutexCopyThreadCount;
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패 횟수 count;
|
||||
// - move 대기 queue 가 0 인 경우 count reset 처리;
|
||||
unsigned int m_uTargetFhsSelectFailCount;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// 생성 가능한 Copy Thread count 를 반환한다.
|
||||
unsigned int GetCopyThreadEnableCount( void );
|
||||
|
||||
// RCDB 로 부터 해당 content를 가진 FHS 정보를 조회하여 반환한다.
|
||||
bool GetMetaHostList( CRebalanceData & data, DataBase & rcdb, std::list<std::string> & listHost );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __COPY_CONTROL_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
#include "CopyThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "../ftsd/FtsdSocketControl.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "ReportLog.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
CCopyThread::CCopyThread( CRebalanceData & data
|
||||
, CDataQueue<CRebalanceData>* const pQueueUpdateStandby
|
||||
, CCopyControlThread * const pControl )
|
||||
: m_data( data ), m_pQueueUpdateStandby( pQueueUpdateStandby ), m_pControl(pControl)
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
m_threadHandle = 0;
|
||||
|
||||
}
|
||||
|
||||
CCopyThread::~CCopyThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
m_pControl = NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
bool CCopyThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueUpdateStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CopyThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
if( m_pControl == NULL )
|
||||
{
|
||||
LOG( LERR, "CopyThread: CCopyControlThread object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업할 data 객체의 각 항목이 유효한지 check
|
||||
|
||||
// 1. copy target host check
|
||||
if( m_data.m_szMoveHost.size() < 7 )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy target host[%s] not valid.", m_data.m_szMoveHost.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. origin host & origin content check
|
||||
if( m_data.m_szOriginHost.size() < 7 || m_data.m_szOriginFileNameHash.size() < 5 )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy origin host[%s], filename[%s] not valid."
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szOriginFileNameHash.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CCopyThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CopyThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
void * CCopyThread::threadFunc( void * arg )
|
||||
{
|
||||
CCopyThread * pObject = reinterpret_cast<CCopyThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
// Thread 동작시 control class 에 count 증가 처리
|
||||
pObject->m_pControl->PlusCopyThreadCount();
|
||||
|
||||
// 실제 작업 수행
|
||||
pObject->Execute();
|
||||
|
||||
// Thread 동작 완료시 control class 에 count 감소 처리
|
||||
pObject->m_pControl->MinusCopyThreadCount();
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
// 객체 자동 delete 처리
|
||||
delete pObject;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CCopyThread::Execute( )
|
||||
{
|
||||
// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
|
||||
CFtsdSocketControl fhs;
|
||||
|
||||
// origin FHS connect
|
||||
bool bResult
|
||||
= fhs.ConnectTarget( m_data.m_szOriginHost, CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
|
||||
|
||||
if( bResult == false )
|
||||
{
|
||||
// origin fhs connect fail.
|
||||
LOG( LERR, "CopyThread: origin fhs[%s][%u] connect fail. [%s]"
|
||||
, m_data.m_szOriginHost.c_str(), CProcessConfig::GetInstance()->GetFhsTransferDaemonPort()
|
||||
, m_data.m_szURI.c_str() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// target FHS 정보를 vector 로 저장 처리
|
||||
std::vector<std::string> vecTargetFHS;
|
||||
vecTargetFHS.clear();
|
||||
vecTargetFHS.push_back( m_data.m_szMoveHost );
|
||||
|
||||
|
||||
// 연결된 FHS 로 content copy 처리 요청 전달.
|
||||
bResult = fhs.SendFileReplicationRequest( m_data.m_szOriginFileNameHash
|
||||
, m_data.m_uOriginContentLength
|
||||
, CProcessConfig::GetInstance()->GetHashCheckLevel()
|
||||
, true
|
||||
, vecTargetFHS );
|
||||
|
||||
if( bResult == false )
|
||||
{
|
||||
// content copy 요청 실패
|
||||
LOG( LERR, "CopyThread: content copy request send fail to origin fhs.[%s][%s]"
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szURI.c_str() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// FHS 로 부터 처리결과 수신 대기
|
||||
|
||||
// response timeout 처리 관련 변수
|
||||
struct timespec timeStart, timeNow;
|
||||
double timeElapsed = 0.0;
|
||||
memset( &timeStart, 0x00, sizeof( struct timespec ) );
|
||||
memset( &timeNow, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeStart );
|
||||
|
||||
|
||||
// 응답 결과 저장을 위한 변수
|
||||
int nResult;
|
||||
bool bSuccess = false;
|
||||
std::string szErrorMessage;
|
||||
std::map<std::string, std::string> mapSuccessFHS;
|
||||
std::map<std::string, std::string> mapFailFHS;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
nResult = fhs.GetFileReplicationResult( 3, bSuccess, szErrorMessage, mapSuccessFHS, mapFailFHS );
|
||||
|
||||
if( nResult == 0 )
|
||||
{
|
||||
// 지정된 시간동안 결과 수신 못한 경우
|
||||
if( fhs.SendAliveCheck() == false )
|
||||
{
|
||||
// 다음 loop 에서 -1 연결 종료 처리되므로.. 여기서는 로깅만..
|
||||
LOG( LERR, "CopyThread: content copy alive check fail.[%s][%s]"
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szURI.c_str() );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// timeout 발생했으므로.. 2 sec 대기 후 다시 시도
|
||||
sleep( 2 );
|
||||
}
|
||||
}
|
||||
else if( nResult == 1 )
|
||||
{
|
||||
// 처리 시간 계산
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
timeElapsed = (double)((timeNow.tv_sec - timeStart.tv_sec) * 1.0e9 + (timeNow.tv_nsec - timeStart.tv_nsec)) / 1.0e9;
|
||||
|
||||
|
||||
// FHS 로 부터 응답 수신
|
||||
if( bSuccess == true )
|
||||
{
|
||||
// Copy 처리 결과가.. mapSuccessFHS 에 저장된 상태면.. 성공
|
||||
// mapFailFHS 에 저장된 상태면 뭔가 오류가 발생한 상황으로 판단해야 함
|
||||
std::map<std::string, std::string>::iterator itFHS;
|
||||
|
||||
if( mapSuccessFHS.empty() == false )
|
||||
{
|
||||
// 성공 FHS 목록에 존재하는 경우... 정상 copy 처리된 것으로 판단
|
||||
itFHS = mapSuccessFHS.begin();
|
||||
|
||||
m_data.m_uMoveResult = MOVE_OK;
|
||||
m_data.m_szMoveHost = itFHS->first;
|
||||
m_data.m_szMoveFileNameHash = itFHS->second;
|
||||
m_data.m_uMoveContentLength = m_data.m_uOriginContentLength;
|
||||
|
||||
// RCDB update queue 에 PUSH 처리
|
||||
if( m_pQueueUpdateStandby->Push( m_data ) == false )
|
||||
{
|
||||
// 실패시 3 sec sleep 후 1회만 재시도.
|
||||
sleep( 3 );
|
||||
if( m_pQueueUpdateStandby->Push( m_data ) == false )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy ok [%.2lf sec]. but update queue push fail. OP manual delete need.[%s %s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szMoveHost.c_str(), m_data.m_szMoveFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 관련 내역 로깅
|
||||
_LOG( LINF, "CopyThread[%lu]: copy ok. origin [%s svc:%s id:%llu size:%llu uri:%s]"
|
||||
, pthread_self()
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szURI.c_str() );
|
||||
_LOG( LINF, "CopyThread[%lu]: copy ok [%.2lf sec]. target [%s size:%llu %s]"
|
||||
, pthread_self()
|
||||
, timeElapsed
|
||||
, m_data.m_szMoveHost.c_str(), m_data.m_uMoveContentLength, m_data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// fail 목록에 포함된 상태.
|
||||
if( mapFailFHS.empty() == false )
|
||||
{
|
||||
itFHS = mapFailFHS.begin();
|
||||
|
||||
m_data.m_uMoveResult = MOVE_ERROR_CREATE;
|
||||
m_data.m_szMoveHost = itFHS->first;
|
||||
szErrorMessage = itFHS->second;
|
||||
|
||||
// 실패했으므로.. RCDB update queue 에 push 하지 않고...
|
||||
// 로깅만 하고 종료한다.
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. [%s svc:%s id:%llu size:%llu]->[%s][%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str(), szErrorMessage.c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, fail map 이 모두 빈 상태.. ?
|
||||
// 이런 상황은 발생하면 안되는데...
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. success-fail map all empty.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// content 이상으로 copy 처리하지 못한 경우.
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. check content [%s svc:%s id:%llu size:%llu]->[%s][%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str(), szErrorMessage.c_str()
|
||||
);
|
||||
|
||||
// source file access 오류인 경우..
|
||||
// - report 로깅
|
||||
if( strncmp( szErrorMessage.c_str(), "source file access fail", 23 ) == 0 )
|
||||
{
|
||||
CReportLog reportLog;
|
||||
std::string errorMessage;
|
||||
errorMessage.clear();
|
||||
|
||||
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
|
||||
{
|
||||
reportLog.GetLastError( errorMessage );
|
||||
LOG( LERR, "CopyThread: report log init failed.[%s]", errorMessage.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
reportLog.Write( "ERR", "[%s] [content access fail. please check. host:%s svc:%s id:%llu uri:'%s' ]"
|
||||
, PROG_NAME
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str(), m_data.m_uResourceId
|
||||
, m_data.GetQueryURI().c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 처리 결과 수신에 따른 loop 종료
|
||||
break;
|
||||
|
||||
}
|
||||
else if( nResult == -1 )
|
||||
{
|
||||
// 처리 시간 계산
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
timeElapsed = (double)((timeNow.tv_sec - timeStart.tv_sec) * 1.0e9 + (timeNow.tv_nsec - timeStart.tv_nsec)) / 1.0e9;
|
||||
|
||||
// FHS 와 연결 끝어진 경우...
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. fhs connection broken.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str() );
|
||||
|
||||
break;
|
||||
}
|
||||
else if( nResult == 2 )
|
||||
{
|
||||
// Replication 응답이 아닌 경우.. 무시
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 미정의된 상황
|
||||
LOG( LERR, "CopyThread: receive undefine result.[%d] [%s][%s][%llu]->[%s]"
|
||||
, nResult
|
||||
, m_data.m_szURI.c_str(), m_data.m_szOriginHost.c_str(), m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str() );
|
||||
break;
|
||||
}
|
||||
|
||||
// Total Reponse 대기 시간 확인
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
timeElapsed = (double)((timeNow.tv_sec - timeStart.tv_sec) * 1.0e9 + (timeNow.tv_nsec - timeStart.tv_nsec)) / 1.0e9;
|
||||
if( timeElapsed > 1200.0 ) // 20분 이상 대기하는 경우 - 장비별 재선택 제한시간이 20 분 정도이므로..
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy fail. response wait %.2lf sec, but timeout 1200 sec.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str() );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} // while(1)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
rc_balanced copy thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/22
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __CONTENT_COPY_THREAD_H__
|
||||
#define __CONTENT_COPY_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
|
||||
// CCopyThread class
|
||||
// - 전달받은 content 정보를 이용하여...
|
||||
// - 실제 FHS 의 ftsd 와 통신을 하여 Content COPY 처리를 수행
|
||||
class CCopyThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CCopyThread(
|
||||
CRebalanceData & data
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CCopyControlThread * const pControl );
|
||||
|
||||
// 소멸자
|
||||
~CCopyThread();
|
||||
|
||||
// Thread 생성 및 Copy 작업 시작
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// 작업 대상 content 정보 저장을 위한 객체
|
||||
CRebalanceData m_data;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// thread 상태를 check 하는 CopyControlThread 객체에 대한 포인터
|
||||
CCopyControlThread * m_pControl;
|
||||
|
||||
private:
|
||||
|
||||
// Thread function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 content copy 처리를 수행
|
||||
void Execute( void );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __CONTENT_COPY_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/****************************************************************************
|
||||
Data Object Queue Template class Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2014/06/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : 개발팀 스토리지 담당.
|
||||
email : storage.sd@solbox.com
|
||||
version : 3.4.0
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
/* 참고사항
|
||||
* 본 Template Class 는 rc_rmcd 의 DataQueue.hpp 에 정의된 소스로서
|
||||
* rc_rmcd revision 1428 에서 복사한 코드임.
|
||||
*/
|
||||
|
||||
#ifndef __DATA_QUEUE_HPP__
|
||||
#define __DATA_QUEUE_HPP__
|
||||
|
||||
#include <queue>
|
||||
#include <pthread.h>
|
||||
|
||||
// Queue default size
|
||||
#define QUEUE_DEFAULT_MAX_SIZE 1024
|
||||
|
||||
template <typename T>
|
||||
class CDataQueue
|
||||
{
|
||||
private:
|
||||
|
||||
// data 객체를 저장할 queue.
|
||||
std::queue<T> m_queue;
|
||||
|
||||
// queue 접근을 제어할 뮤텍스 lock.
|
||||
pthread_mutex_t m_mutexQueue;
|
||||
|
||||
// queue 의 최대 크기 제한값 저장 변수.
|
||||
unsigned int m_uMaxSize;
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CDataQueue() {
|
||||
m_uMaxSize = QUEUE_DEFAULT_MAX_SIZE;
|
||||
pthread_mutex_init( &m_mutexQueue, NULL );
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
~CDataQueue() {
|
||||
pthread_mutex_destroy( &m_mutexQueue );
|
||||
}
|
||||
////////////////////////////////////
|
||||
|
||||
// queue 크기를 반환
|
||||
unsigned int GetSize( void );
|
||||
|
||||
// queue Max 크기를 반환
|
||||
unsigned int GetMaxSize( void );
|
||||
|
||||
// queue Max 크기를 설정
|
||||
void SetMaxSize( unsigned int uMaxSize );
|
||||
|
||||
// 초기화
|
||||
void Init( void );
|
||||
|
||||
// Data Object 저장
|
||||
bool Push( T const& object );
|
||||
|
||||
// queue 에 저장된 첫번재 element 반환
|
||||
// - queue 크기가 0 인 경우 호출시 std::out_of_range 오류가 발생해야 하지만...
|
||||
// 예외 처리를 대부분 하지 않으므로... 빈 객체를 반환처리하도록 함.
|
||||
// 따라서...꼭 GetSize() 함수를 호출하여 data 가 존재하는지 확인한 후 본 함수 호출할 것.
|
||||
T Front( void );
|
||||
|
||||
// queue 에 저장된 첫번재 element 제거
|
||||
void Pop( void );
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
// queue 크기를 반환
|
||||
template <typename T>
|
||||
unsigned int CDataQueue<T>::GetSize( void )
|
||||
{
|
||||
return (unsigned int)m_queue.size();
|
||||
}
|
||||
|
||||
// queue Max 크기를 반환
|
||||
template <typename T>
|
||||
unsigned int CDataQueue<T>::GetMaxSize( void )
|
||||
{
|
||||
return m_uMaxSize;
|
||||
}
|
||||
|
||||
// queue Max 크기를 설정
|
||||
template <typename T>
|
||||
void CDataQueue<T>::SetMaxSize( unsigned int uMaxSize )
|
||||
{
|
||||
m_uMaxSize = uMaxSize;
|
||||
}
|
||||
|
||||
// 초기화
|
||||
template <typename T>
|
||||
void CDataQueue<T>::Init( void )
|
||||
{
|
||||
// queue clear 처리를 수행.
|
||||
// - queue 의 경우 clear 함수가 지원되지 않으므로 size 가 0 이 될때까지 pop 처리
|
||||
pthread_mutex_lock( &m_mutexQueue );
|
||||
|
||||
while( m_queue.empty() == false )
|
||||
m_queue.pop();
|
||||
|
||||
pthread_mutex_unlock( &m_mutexQueue );
|
||||
return;
|
||||
}
|
||||
|
||||
// Data Object 저장
|
||||
template <typename T>
|
||||
bool CDataQueue<T>::Push( T const& object )
|
||||
{
|
||||
// 현재 queue size 가 설정된 최대 size 를 이상인 경우 실패 처리.
|
||||
if( m_queue.size() >= m_uMaxSize )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexQueue );
|
||||
m_queue.push( object );
|
||||
pthread_mutex_unlock( &m_mutexQueue );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// queue 에 저장된 첫번재 element 반환
|
||||
template <typename T>
|
||||
T CDataQueue<T>::Front( void )
|
||||
{
|
||||
// queue 특성상 pop, front 처리시 queue 가 비어있지 않음을 보장해야 한다.
|
||||
// - 만약 queue 가 빈 경우.. std::out_of_range 예외를 발생시켜야 하지만...
|
||||
// 호출 함수에서 이런 예외를 처리하지 않으므로...
|
||||
// 빈 깡통 객체를 전달한다.
|
||||
|
||||
T object;
|
||||
if( m_queue.empty() == false )
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexQueue );
|
||||
object = m_queue.front();
|
||||
pthread_mutex_unlock( &m_mutexQueue );
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
// queue 에 저장된 첫번재 element 제거
|
||||
template <typename T>
|
||||
void CDataQueue<T>::Pop( void )
|
||||
{
|
||||
// queue 특성상 pop, front 처리시 queue 가 비어있지 않음을 보장해야 한다.
|
||||
if( m_queue.empty() == false )
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexQueue );
|
||||
m_queue.pop();
|
||||
pthread_mutex_unlock( &m_mutexQueue );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* __DATA_QUEUE_HPP__ */
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/***************************************************************************
|
||||
Database Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2010 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2010 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
#include "Database.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
DataBase::~DataBase()
|
||||
{
|
||||
if(m_PGconn != NULL) {
|
||||
PgCloseDB();
|
||||
}
|
||||
}
|
||||
|
||||
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
|
||||
{
|
||||
ostringstream conninfo;
|
||||
|
||||
// CHG 2014-10-29 huibong
|
||||
// application_name add to logging RCDB
|
||||
|
||||
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
|
||||
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
|
||||
" application_name=" << PROG_NAME;
|
||||
|
||||
#ifdef _USE_LIBPQ_KEEPALIVE
|
||||
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
|
||||
conninfo <<" keepalives=1";
|
||||
#endif // _USE_LIBPQ_KEEPALIVE
|
||||
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
|
||||
m_PGconn = PQconnectdb(conninfo.str().c_str());
|
||||
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_PGconn;
|
||||
}
|
||||
|
||||
PGconn *DataBase::PgOpenDB(const char *pszDBName)
|
||||
{
|
||||
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
|
||||
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
|
||||
return NULL;
|
||||
}
|
||||
return m_PGconn;
|
||||
}
|
||||
|
||||
void DataBase::PgCloseDB()
|
||||
{
|
||||
if(m_PGconn != NULL)
|
||||
{
|
||||
PQfinish(m_PGconn);
|
||||
m_PGconn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int DataBase::PgResult(CFLAG flag)
|
||||
{
|
||||
int fRet = 0;
|
||||
int Result;
|
||||
|
||||
Result = PQresultStatus(m_pRes);
|
||||
switch(Result) {
|
||||
case PGRES_EMPTY_QUERY :
|
||||
fRet = -1;
|
||||
break;
|
||||
case PGRES_BAD_RESPONSE :
|
||||
fRet = -2;
|
||||
break;
|
||||
case PGRES_NONFATAL_ERROR :
|
||||
fRet = -3;
|
||||
break;
|
||||
case PGRES_FATAL_ERROR :
|
||||
fRet = -4;
|
||||
break;
|
||||
case PGRES_TUPLES_OK :
|
||||
fRet = 1;
|
||||
break;
|
||||
case PGRES_COMMAND_OK :
|
||||
fRet = 2;
|
||||
break;
|
||||
}
|
||||
if(fRet < 0) {
|
||||
m_ErrorMessage = PQresultErrorMessage(m_pRes);
|
||||
}
|
||||
m_ResultCode = Result;
|
||||
if(flag == CLEAR && m_pRes) {
|
||||
PQclear(m_pRes);
|
||||
m_pRes = NULL;
|
||||
}
|
||||
return fRet;
|
||||
}
|
||||
|
||||
string &DataBase::GetErrorMessage()
|
||||
{
|
||||
return m_ErrorMessage;
|
||||
}
|
||||
|
||||
int DataBase::GetCmdTuples()
|
||||
{
|
||||
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
|
||||
return (int)atoi(PQcmdTuples(m_pRes));
|
||||
}
|
||||
|
||||
int DataBase::GetNoTuples()
|
||||
{
|
||||
return PQntuples(m_pRes);
|
||||
}
|
||||
|
||||
int DataBase::GetNoFields()
|
||||
{
|
||||
return PQnfields(m_pRes);
|
||||
}
|
||||
|
||||
PGresult *DataBase::GetRes()
|
||||
{
|
||||
return m_pRes;
|
||||
}
|
||||
|
||||
void DataBase::PgClear()
|
||||
{
|
||||
if(m_pRes)
|
||||
{
|
||||
PQclear(m_pRes);
|
||||
m_pRes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char *DataBase::GetValue(int tuple, int field)
|
||||
{
|
||||
return PQgetvalue(m_pRes, tuple, field);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(char *pszQuery)
|
||||
{
|
||||
return this->PgDoExec(pszQuery, NOT_CLEAR);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(string &strQuery)
|
||||
{
|
||||
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
|
||||
}
|
||||
|
||||
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
|
||||
{
|
||||
if(m_PGconn == NULL) return -1;
|
||||
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
|
||||
m_pRes = PQexec(m_PGconn, pszQuery);
|
||||
int r = PgResult(flag);
|
||||
return r;
|
||||
}
|
||||
|
||||
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
|
||||
{
|
||||
if(m_PGconn == NULL) return -1;
|
||||
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
|
||||
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
|
||||
int r = PgResult(flag);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
|
||||
PQescapeString(to, from, length);
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/***************************************************************************
|
||||
Database Class
|
||||
-----------------------------------------
|
||||
begin : 2010/03/11
|
||||
copyright : (C) 2010 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2010 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.
|
||||
***************************************************************************/
|
||||
#ifndef __POSTGRESQL_DATABASE_H__
|
||||
#define __POSTGRESQL_DATABASE_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "libpq-fe.h"
|
||||
#define MaxSizeOfDBQuery 1024*9
|
||||
|
||||
using namespace std;
|
||||
class DataBase
|
||||
{
|
||||
public:
|
||||
enum CFLAG { CLEAR = 1, NOT_CLEAR };
|
||||
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
|
||||
~DataBase();
|
||||
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
|
||||
PGconn *PgOpenDB(const char *pszDBName);
|
||||
void PgCloseDB();
|
||||
int PgResult(CFLAG flag);
|
||||
PGconn *GetPgConn(){ return m_PGconn;}
|
||||
PGresult *GetRes();
|
||||
void SetRes(PGresult * v) {m_pRes = v;}
|
||||
int GetCmdTuples();
|
||||
int GetNoTuples();
|
||||
int GetNoFields();
|
||||
int GetResultCode() { return m_ResultCode; }
|
||||
char *GetValue(int tuple, int field);
|
||||
void PgClear();
|
||||
int PgDoExec(string &strQuery);
|
||||
int PgDoExec(char *pszQuery);
|
||||
int PgDoExec(char *pszQuery, CFLAG flag);
|
||||
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
|
||||
int PgEscapeString(char *to, const char *from, size_t length);
|
||||
string &GetErrorMessage();
|
||||
private:
|
||||
int m_ResultCode;
|
||||
PGconn *m_PGconn;
|
||||
PGresult *m_pRes;
|
||||
string m_ErrorMessage;
|
||||
};
|
||||
|
||||
#endif // ~__POSTGRESQL_DATABASE_H__
|
||||
@@ -0,0 +1,464 @@
|
||||
#include "Main.h"
|
||||
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "WorkerRebalance.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// 실제 content 분산 처리를 담당하는 Rebalance Worker PID 정보.
|
||||
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
|
||||
pid_t g_pidWorkerRebalance = -1;
|
||||
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
// 설정 파일을 저장할 변수.
|
||||
std::string strConfigFileName = DEFAULT_CONFIG_FILE;
|
||||
|
||||
// linux 에서 프로세스를 Titile 변경을 지원을 위해서 argv 메모리에 저장
|
||||
argv = save_ps_display_args( argc, argv );
|
||||
|
||||
// 전달 받은 옵션 여부 확인 및 처리
|
||||
if( argc >= 2 )
|
||||
{
|
||||
int opt;
|
||||
while( ( opt = getopt( argc, argv, "hvc:" ) ) != -1 ) // 옵션 끝까지 파싱처리
|
||||
{
|
||||
switch( opt )
|
||||
{
|
||||
case 'h':
|
||||
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
|
||||
PrintUsage();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 'c':
|
||||
strConfigFileName = optarg;
|
||||
fprintf( stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str() );
|
||||
break;
|
||||
|
||||
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
|
||||
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 프로세스 중복 실행 여부 검사
|
||||
if( IsCurrentProcessRun() == true )
|
||||
{
|
||||
fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::string szErrorMessage;
|
||||
|
||||
// conf 로딩 및 검사
|
||||
if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
|
||||
{
|
||||
fprintf( stderr, "[ERR] %s\n\n", szErrorMessage.c_str() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Log 객체 생성 및 초기화
|
||||
if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
|
||||
{
|
||||
fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Daemonize...
|
||||
if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
|
||||
{
|
||||
fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// signal 처리 설정
|
||||
SetSignalMain();
|
||||
|
||||
|
||||
// Process 기동 관련 정보 기록 -> Log
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
_LOG( LINF, "Config : %s", strConfigFileName.c_str() );
|
||||
_LOG( LINF, "Log : %s/%s", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
|
||||
_LOG( LINF, "Log level : %d", CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
_LOG( LINF, "RC ID : %s", CProcessConfig::GetInstance()->GetRcId() );
|
||||
_LOG( LINF, "RCDB : %s %u %s %s", CProcessConfig::GetInstance()->GetRcdbIp()
|
||||
, CProcessConfig::GetInstance()->GetRcdbPort()
|
||||
, CProcessConfig::GetInstance()->GetRcdbName()
|
||||
, CProcessConfig::GetInstance()->GetRcdbAcct() );
|
||||
|
||||
_LOG( LINF, "FHS ftsd tcp port : %u", CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
|
||||
_LOG( LINF, "Max queue size : %u", CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
_LOG( LINF, "Max copy thread count : %d", MAX_COPY_THREAD_COUNT );
|
||||
_LOG( LINF, "FHS reselection time limit : %u (min)", CProcessConfig::GetInstance()->GetFhsReselectionTimeLimit() );
|
||||
_LOG( LINF, "Move content hash check : %u (0:not check, 1:partitial, 2:full)", CProcessConfig::GetInstance()->GetHashCheckLevel() );
|
||||
_LOG( LINF, "Target FHS disk busy limit : %.1lf %%", CProcessConfig::GetInstance()->GetTargetFhsMaxDiskBusyLimit() );
|
||||
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
|
||||
// Log Level 재설정. -> conf 설정대로 변경 처리.
|
||||
#ifdef _DEBUG_
|
||||
CLogger::GetInstance()->SetLogLevel( LDBG );
|
||||
#else
|
||||
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
#endif
|
||||
|
||||
|
||||
// Worker Process 생성
|
||||
// 실제 Content 분산 처리를 수행할 Worker 프로세스 생성.
|
||||
if( MakeWorkerRebalance() == false )
|
||||
{
|
||||
LOG( LERR, "[Main] Rebalance worker process create failed. => Process exit." );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// Process Rename..
|
||||
set_ps_display( PROG_NAME": Main", false );
|
||||
|
||||
|
||||
// Main Process : 그냥 대기
|
||||
while( 1 )
|
||||
{
|
||||
// Main Process 는 할 일이 없다.
|
||||
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
|
||||
// => 따라서 그냥 시그널 대기
|
||||
pause();
|
||||
}
|
||||
|
||||
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
|
||||
ReadyToExit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExit()
|
||||
{
|
||||
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 사용방법 표시
|
||||
void PrintUsage()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n" );
|
||||
fprintf( stderr, "Options: \n" );
|
||||
fprintf( stderr, " -h Display help information \n" );
|
||||
fprintf( stderr, " -v Display version \n" );
|
||||
fprintf( stderr, " -c {file} Use {file} as config file \n" );
|
||||
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
|
||||
fprintf( stderr, " - automatically content rebalancing in FHS saved content.\n" );
|
||||
fprintf( stderr, " - rc_mond must be running first.\n\n" );
|
||||
|
||||
fprintf( stderr, "process title explain.\n" );
|
||||
fprintf( stderr, " - S: move standby content count.\n" );
|
||||
fprintf( stderr, " - T: content transfer job count.\n" );
|
||||
fprintf( stderr, " - U: copy complete content count. DB update standby.\n" );
|
||||
fprintf( stderr, " - D: unlink standby content count.\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 버전 정보 표시
|
||||
void PrintVersion()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun( )
|
||||
{
|
||||
char tempBuffer[512];
|
||||
FILE * fd = NULL;
|
||||
bool bRun = false;
|
||||
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s | sort", PROG_NAME );
|
||||
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;
|
||||
|
||||
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
std::string tempPid( tempBuffer );
|
||||
Trim( tempPid );
|
||||
|
||||
if( atoi( tempPid.c_str() ) != getpid() )
|
||||
{
|
||||
std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;
|
||||
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @return void
|
||||
void Trim( std::string & str )
|
||||
{
|
||||
if( str.length() == 0 )
|
||||
return;
|
||||
|
||||
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
|
||||
std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
|
||||
if( pos != std::string::npos )
|
||||
str.erase( pos + 1 );
|
||||
|
||||
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
|
||||
pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
|
||||
if( pos != std::string::npos )
|
||||
str.erase( 0, pos );
|
||||
}
|
||||
|
||||
// Worker 프로세스 종료 신호 수신시
|
||||
static void SignalWorkerDead( int nSignalNumber )
|
||||
{
|
||||
pid_t deadPid;
|
||||
int nDeadStatus;
|
||||
|
||||
while( ( deadPid = waitpid( -1, &nDeadStatus, WNOHANG ) ) > 0 )
|
||||
{
|
||||
// Worker 프로세스가 signal 에 의해 종료되었는지 검사.
|
||||
// - 종료 사유 로깅 및
|
||||
// - Worker 의 초기화 실패로 인한 종료시... Main 프로세스가 종료되도록 처리.
|
||||
if( WIFSIGNALED( nDeadStatus ) )
|
||||
{
|
||||
// signal 에 의해 종료된 경우...
|
||||
// - 관련 내역 로깅
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalance worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// signal 에 종료된 상황이 아닌 경우...
|
||||
// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
|
||||
// - 기타 등등.
|
||||
|
||||
// 관련 내역 로깅.
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalance worker process[%d] killed. Not signal.", deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] killed. Not signal", deadPid );
|
||||
}
|
||||
|
||||
// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
|
||||
// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
|
||||
if( WIFEXITED( nDeadStatus ) )
|
||||
{
|
||||
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
|
||||
{
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalace worker process[%d] initialize failed.", deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] exit with fail.", deadPid );
|
||||
}
|
||||
|
||||
_LOG( LERR, "[Main] Program abnormally exited because Worker initialize faild.." );
|
||||
|
||||
// Main 종료 시그널 발생
|
||||
raise( SIGTERM );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행했으므로...
|
||||
// 죽은 Worker 프로세스를 재생성 처리한다.
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
if( MakeWorkerRebalance() == false )
|
||||
{
|
||||
_LOG( LERR, "[Main] Rebalance worker process recreate failed.=> Main process exit." );
|
||||
raise( SIGTERM );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Worker process recreate success." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 오류 발생시 해당 내역 로깅
|
||||
if( deadPid < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main] SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Main 프로세스에 대한 종료 처리 수신시
|
||||
static void SignalMainTerminate( int nSignalNumber )
|
||||
{
|
||||
// Rebalance worker 프로세스를 종료 처리한다.
|
||||
if( g_pidWorkerRebalance != -1 )
|
||||
{
|
||||
kill( g_pidWorkerRebalance, SIGTERM );
|
||||
}
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LINF, "[Main:%d] Process exit by SIGTERM signal. Good Bye..", getpid());
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main:%d] Process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
// 잠시 대기
|
||||
sleep( 1 );
|
||||
|
||||
// 기타 모듈들의 종료 처리 수행
|
||||
ReadyToExit();
|
||||
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
|
||||
// Main Process 의 signal 처리기...
|
||||
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
|
||||
void SetSignalMain()
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
memset( &act, 0x00, sizeof( act ) );
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
// 무시 처리 signal
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
|
||||
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Worker child process 종료에 대한 처리기 설정.
|
||||
act.sa_handler = SignalWorkerDead;
|
||||
sigaction( SIGCHLD, &act, NULL );
|
||||
|
||||
// 사용자의 종료 또는 에러 관련 신호 처리.
|
||||
act.sa_handler = SignalMainTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
}
|
||||
|
||||
|
||||
// 실제 Content 분산을 담당할 Worker 프로세스 생성.
|
||||
bool MakeWorkerRebalance()
|
||||
{
|
||||
// fork() 결과를 Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장
|
||||
g_pidWorkerRebalance = fork();
|
||||
|
||||
if( g_pidWorkerRebalance == 0 )
|
||||
{
|
||||
// 생성된 Worker 프로세스인 경우...
|
||||
int iResult;
|
||||
|
||||
// Rebalance Worker 프로세스의 Main 함수 호출
|
||||
iResult = WorkerRebalanceMain();
|
||||
|
||||
// Worker 프로세스의 Main 함수 종료시 종료 처리
|
||||
_LOG( LINF, "[Main] Rebalance worker process exit. PID[%d] return[%d]", getpid(), iResult );
|
||||
|
||||
ReadyToExit();
|
||||
exit( iResult );
|
||||
}
|
||||
else if( g_pidWorkerRebalance < 0 )
|
||||
{
|
||||
// fork 실패시
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main] Rebalance worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 현재의 Main 프로세스
|
||||
_LOG( LINF, "[Main] Rebalance worker process created.. PID[%d]", g_pidWorkerRebalance );
|
||||
|
||||
// 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 200000000; // 0.2 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
Main.h for rc_balanced
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/22
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __RC_BALANCED_MAIN_H__
|
||||
#define __RC_BALANCED_MAIN_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief 사용 방법을 화면에 표시하기 위한 함수.
|
||||
void PrintUsage( void );
|
||||
|
||||
/// @brief Version 정보를 화면에 표시하기 위한 함수
|
||||
void PrintVersion( void );
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun( void );
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
|
||||
/// @return void
|
||||
void Trim( std::string & str );
|
||||
|
||||
/// @brief 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
|
||||
void ReadyToExit( void );
|
||||
|
||||
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalMain( void );
|
||||
|
||||
/// @brief 실제 content 분산 처리를 담당할 Worker 프로세스 생성.
|
||||
/// @return 생성 성공시 true, 실패시 false
|
||||
bool MakeWorkerRebalance();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __RC_BALANCED_MAIN_H__ */
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#****************************************************************************
|
||||
# Makefile for rc_balanced
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2019/02/22
|
||||
# copyright : (C) 2005 Solbox Inc.
|
||||
# author : Dev Storage Team
|
||||
# email : huibong@solbox.com
|
||||
# version : 3.5
|
||||
#
|
||||
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
# Redistribution and use in source and binary forms, with or with out
|
||||
# modification, are not permitted in outside of Solbox Inc.
|
||||
#****************************************************************************
|
||||
|
||||
|
||||
# Program info
|
||||
PROG_NAME = rc_balanced
|
||||
REVISION = 1531
|
||||
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
|
||||
|
||||
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
|
||||
|
||||
# COPY 처리를 수행하는 Copy Thread 의 Max count 값 설정
|
||||
# - 3 이상 값으로 할 경우.. MOVE 수신 FHS 가 1대인 경우.. Write 처리 관련 부하 발생
|
||||
# - recommand: 1~2 , max: 10
|
||||
MAX_COPY_THREAD_COUNT = 2
|
||||
|
||||
|
||||
INSTALL_BIN = /user/service/bin
|
||||
INSTALL_CONF = /user/service/etc
|
||||
|
||||
# Compiler info
|
||||
CC = /usr/bin/g++
|
||||
|
||||
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized \
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual \
|
||||
-fno-rtti -D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
|
||||
LFLAGS =
|
||||
|
||||
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\" -DMAX_COPY_THREAD_COUNT=$(MAX_COPY_THREAD_COUNT)
|
||||
|
||||
# Application Enviroment
|
||||
APP = $(PROG_NAME)
|
||||
|
||||
DIR_INCLUDE = -I../lib -I../ftsd -I../rc_mond -I/user/db/pgsql/include
|
||||
DIR_LIB = -L../lib
|
||||
|
||||
|
||||
OBJ = ProcessConfig.o RebalanceData.o Common.o \
|
||||
../ftsd/FtsdSocketControl.o RcdbInfo.o Database.o CopyThread.o ShmInfoThread.o \
|
||||
ProcessMonitoringThread.o TargetSelectThread.o CopyControlThread.o RcdbUpdateThread.o PhysicalDeleteThread.o \
|
||||
WorkerRebalance.o \
|
||||
Main.o
|
||||
|
||||
LIBS = ../lib/libInterCommon.a -lpthread -lrt /user/db/pgsql/lib/libpq.a
|
||||
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
all:$(APP)
|
||||
sync
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
|
||||
$(PROG_NAME): $(OBJ)
|
||||
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
|
||||
|
||||
|
||||
clean:
|
||||
-rm -f *.o core.* *.out *.log
|
||||
-rm -f ../ftsd/*.o ../ftsd/*.out ../ftsd/core.*
|
||||
-rm -f ../rc_mond/*.o ../rc_mond/*.out ../rc_mond/core.*
|
||||
-rm -f $(APP)
|
||||
sync
|
||||
|
||||
|
||||
install : $(APP)
|
||||
-cp $(PROG_NAME) $(INSTALL_BIN)/$(PROG_NAME)
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
#include "PhysicalDeleteThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "../ftsd/FtsdSocketControl.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 10
|
||||
#define SLEEP_TIME_MAX 20
|
||||
|
||||
// unlink 처리 실패시 retry queue 에 얼마동안 대기할 것인지 시간 (sec 단위)
|
||||
#define RETRY_STANDBY_MAX_TIME 1800
|
||||
|
||||
// unlink 처리 실패시 retry 가능 횟수, 최초 실패 후 2회까지 RETRY 한다는 의미
|
||||
#define FAIL_MAX_COUNT 3
|
||||
|
||||
|
||||
CPhysicalDeleteThread::CPhysicalDeleteThread( CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CProcessMonitoringThread * pMonitoringThread )
|
||||
: m_pQueueDeleteStandby( pQueueDeleteStandby ), m_pMonitoringThread( pMonitoringThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// 내부 retry queue 의 최대 크기 설정 (장기 보관 땜시 기본 설정의 2배)
|
||||
m_queueDeleteRetry.SetMaxSize( 2 * (CProcessConfig::GetInstance()->GetMaxQueueSize()) );
|
||||
}
|
||||
|
||||
CPhysicalDeleteThread::~CPhysicalDeleteThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
if( m_pMonitoringThread != NULL )
|
||||
m_pMonitoringThread = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CPhysicalDeleteThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "PhysicalDeleteThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
if( m_pMonitoringThread == NULL )
|
||||
{
|
||||
LOG( LERR, "PhysicalDeleteThread: CProcessMonitoringThread object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CPhysicalDeleteThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "PhysicalDeleteThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CPhysicalDeleteThread::threadFunc( void * arg )
|
||||
{
|
||||
CPhysicalDeleteThread * pObject = reinterpret_cast<CPhysicalDeleteThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CPhysicalDeleteThread::Execute()
|
||||
{
|
||||
// retry queue 및 delete queue 가 비어 있다먼.. MAX sleep 한다.
|
||||
if( m_queueDeleteRetry.GetSize() == 0 && m_pQueueDeleteStandby->GetSize() == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[DELETE ] standby, retry queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[DELETE ] start." );
|
||||
|
||||
CRebalanceData data;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
// 삭제 처리할 content 를 queue 에서 pop 하여 가져온다.
|
||||
// pop 할 content 가 없으면.. sleep time 조정 후 loop 종료
|
||||
if( Pop( data ) == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[DELETE ] proper content not found. max sleep %u sec.", m_uSleep );
|
||||
break;
|
||||
}
|
||||
|
||||
// ftsd 와 통신을 통해 unlink 처리를 수행한다.
|
||||
|
||||
if( Delete( data ) == true )
|
||||
{
|
||||
// unlink 성공
|
||||
// -> Origin content 를 삭제한 경우 Process 모니터링 모듈에 삭제한 FHS, size 정보 전송
|
||||
// -> move 처리 실패로 삭제한 내역은 Process 모니터링 모듈에 전송하지 않는다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// Process monitoring 모듈에 삭제한 FHS, size 관련 정보 전달 기능 추가
|
||||
m_pMonitoringThread->UpdateDeletedInfo( data );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlink 실패
|
||||
// -> Retry queue 에 다시 넣던지.. 아니면 버리던지 한다.
|
||||
data.m_uDeleteFailCount +=1;
|
||||
if( data.m_uDeleteFailCount > FAIL_MAX_COUNT )
|
||||
{
|
||||
// 재시도 횟수 초과인 경우... 로깅 후 버린다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry count over.[origin %s %s %llu]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry count over.[target %s %s %llu]"
|
||||
, data.m_szMoveHost.c_str()
|
||||
, data.m_szMoveFileNameHash.c_str()
|
||||
, data.m_uMoveContentLength );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// retry queue 에 대한 push 처리한다.
|
||||
data.m_timeLastDeleteFail = time( 0 );
|
||||
|
||||
if( m_queueDeleteRetry.Push( data ) == false )
|
||||
{
|
||||
// push 실패 로깅 후 버린다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry queue push fail.[origin %s size:%llu %s]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_uOriginContentLength
|
||||
, data.m_szOriginFileNameHash.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry queue push fail.[target %s size:%llu %s]"
|
||||
, data.m_szMoveHost.c_str()
|
||||
, data.m_uMoveContentLength
|
||||
, data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // unlink 실패
|
||||
|
||||
} // while(1)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CPhysicalDeleteThread::Pop( CRebalanceData & data )
|
||||
{
|
||||
time_t timeNow;
|
||||
|
||||
// 현재 시간 정보 추출
|
||||
time( &timeNow );
|
||||
|
||||
// 우선 retry queue 에서 대상이 존재하는지 확인하여 pop 처리
|
||||
while( m_queueDeleteRetry.GetSize() > 0 )
|
||||
{
|
||||
data = m_queueDeleteRetry.Front();
|
||||
|
||||
// 재시도 횟수값 확인
|
||||
// - 실제로는 queue 에 push 처리할 때 확인해서 처리하지만....
|
||||
// - 혹시 오류가 발생할 수 있어 방어 목적으로 확인한다.
|
||||
if( data.m_uDeleteFailCount > FAIL_MAX_COUNT )
|
||||
{
|
||||
// 재시도 횟수가 over 된 경우 버리고.. 다시 pop
|
||||
m_queueDeleteRetry.Pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete 처리 관련 마지막 실패 시각을 확인하여...
|
||||
// 지정된 대기 시간이 안 된 경우...
|
||||
if( (timeNow - data.m_timeLastDeleteFail) < RETRY_STANDBY_MAX_TIME )
|
||||
{
|
||||
// retry queue 검색을 중단한다.
|
||||
break;
|
||||
}
|
||||
|
||||
// retry queue 에서 해당 content 를 POP 하고...
|
||||
// delete 작업을 수행하도록 true 를 반환한다.
|
||||
m_queueDeleteRetry.Pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 삭제 대기 queue 에서 pop 처리 수행
|
||||
while( m_pQueueDeleteStandby->GetSize() > 0 )
|
||||
{
|
||||
data = m_pQueueDeleteStandby->Front();
|
||||
m_pQueueDeleteStandby->Pop();
|
||||
|
||||
// 해당 객체의 처리 결과를 확인하여 삭제 작업이 필요없는 content 인 경우....
|
||||
// 버리고 다시 pop 한다.
|
||||
if( data.m_uMoveResult == MOVE_ERROR_CREATE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// queue 에서 적합한 content 를 pop 하지 못한 경우... false 반환.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CPhysicalDeleteThread::Delete( CRebalanceData & data )
|
||||
{
|
||||
// ftsd 와 통신할 socket 객체 생성
|
||||
CFtsdSocketControl ftsd;
|
||||
|
||||
// 작업 대상 content 정보를 저장할 변수
|
||||
std::string szHost;
|
||||
std::string szFileNameHash;
|
||||
unsigned long long uContentLength;
|
||||
unsigned long long uResourceId;
|
||||
|
||||
// 전달받은 content 의 처리 결과를 확인하여..어떤 물리 파일을 삭제할 것인지 판단하여 처리
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// origin 의 content 를 삭제하도록 한다.
|
||||
szHost = data.m_szOriginHost;
|
||||
szFileNameHash = data.m_szOriginFileNameHash;
|
||||
uContentLength = data.m_uOriginContentLength;
|
||||
uResourceId = data.m_uResourceId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// MOVE 처리 실패로.. target fhs 에 잘못 생성된 content 를 삭제해야 하는 경우..
|
||||
szHost = data.m_szMoveHost;
|
||||
szFileNameHash = data.m_szMoveFileNameHash;
|
||||
uContentLength = data.m_uMoveContentLength;
|
||||
uResourceId = 0;
|
||||
}
|
||||
|
||||
// FHS 의 ftsd 에 접속
|
||||
if( ftsd.ConnectTarget( szHost, (CProcessConfig::GetInstance()->GetFhsTransferDaemonPort()) ) == false )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] fhs: %s (%s) connect fail."
|
||||
, szHost.c_str(), (data.m_uMoveResult == MOVE_OK ? "origin" : "target") );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 접속한 ftsd 로 unlink 처리 요청 전달.
|
||||
if( ftsd.SendFileUnlinkRequest( szFileNameHash, uContentLength, true ) == false )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink request send fail. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 정보 로깅
|
||||
_LOG( LINF, "[DELETE ] unlink request send ok. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
|
||||
|
||||
int nTimeout = 5; // 응답 대기 시간 (5 sec)
|
||||
int nResult = 0;
|
||||
bool bResultSuccess; // 처리 결과의 성공, 실패 여부를 저장하기 위한 변수
|
||||
std::string szErrorMessage;
|
||||
|
||||
// ftsd 로 부터 응답 수신 대기
|
||||
while( 1 )
|
||||
{
|
||||
// nTimeout 에 지정된 시간동안 응답 대기 시도
|
||||
nResult = ftsd.GetFileUnlinkResult( nTimeout, bResultSuccess, szErrorMessage );
|
||||
|
||||
if( nResult == 1 )
|
||||
{
|
||||
// 요청에 대한 처리 결과 수신
|
||||
if( bResultSuccess == true )
|
||||
{
|
||||
// 요청에 대한 처리가 정상적으로 처리된 경우
|
||||
_LOG( LINF, "[DELETE ] unlink ok. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 오류 발생시
|
||||
_LOG( LERR, "[DELETE ] unlink error.[%s %s id:%llu size:%llu %s] [%s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str(), szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 응답 대기 루프 종료
|
||||
break;
|
||||
}
|
||||
else if( nResult == -1 )
|
||||
{
|
||||
// socket 오류, 접속 종료 등이 발생한 경우
|
||||
_LOG( LERR, "[DELETE ] ftsd reponse wait fail.[%s %s]"
|
||||
, szHost.c_str(), szFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우
|
||||
// -> Allive Check 패킷 한번 쏘고 다시 Loop 로
|
||||
if( ftsd.SendAliveCheck() == false )
|
||||
{
|
||||
// alive 전송 실패
|
||||
// -> socket 종료 또는 오류 발생한 경우
|
||||
_LOG( LERR, "[DELETE ] ftsd reponse wait fail2.[%s %s]"
|
||||
, szHost.c_str(), szFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
// 응답대기
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlink 요청에 대한 응답 패킷이 아닌 경우
|
||||
// -> 무시하고 다시 대기
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content physical delete thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __PHYSICAL_DELETE_THREAD_H__
|
||||
#define __PHYSICAL_DELETE_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "ProcessMonitoringThread.h"
|
||||
|
||||
|
||||
// CPhysicalDeleteThread class
|
||||
// - delete standby queue 에 들어 있는 content 를 pop 하여...
|
||||
// - 각 FHS 의 ftsd 로 unlink 처리 요청을 전송한다.
|
||||
// - 만약 unlink 처리 실패 발생한 경우.. retry queue 에 저장한 후 지정된 시간 이후 재시도 처리한다.
|
||||
class CPhysicalDeleteThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CPhysicalDeleteThread( CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CProcessMonitoringThread * pMonitoringThread );
|
||||
|
||||
// 소멸자
|
||||
~CPhysicalDeleteThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// unlink 처리 실패시 retry 처리를 수행하기 위한 내부 queue 객체 (외부에서 전달받은 객체가 아닌 내부 멤버객체임)
|
||||
CDataQueue<CRebalanceData> m_queueDeleteRetry;
|
||||
|
||||
|
||||
// CProcessMonitoringThread 객체에 대한 포인터
|
||||
// - delete 작업 통계 정보 업데이트를 위한 객체 포인터, 생성자에서 전달받음
|
||||
CProcessMonitoringThread * m_pMonitoringThread;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
|
||||
// Delete queue , Retry queue 에서 삭제 작업을 수행할 content 를 POP 처리하여
|
||||
// 전달받은 참조 인자로 전달한다.
|
||||
// pop 처리할 content 가 없는 경우 false 반환.
|
||||
bool Pop( CRebalanceData &data );
|
||||
|
||||
// 실제 ftsd 와 통신을 통해 인자로 전달받은 content unlink 처리 수행
|
||||
bool Delete( CRebalanceData &data );
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PHYSICAL_DELETE_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 default 값 추가
|
||||
#define DEFAULT_TARGET_FHS_MAX_DISK_BUSY_LIMIT 50.0
|
||||
|
||||
|
||||
// static 변수 초기화
|
||||
CProcessConfig * CProcessConfig::m_pInstance = NULL;
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessConfig::CProcessConfig()
|
||||
{
|
||||
// default 값 설정.
|
||||
|
||||
// Max Queue Size ( default: 4096 )
|
||||
m_nMaxQueueSize = 4096;
|
||||
|
||||
// FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
m_nFhsTransferDaemonPort = 14001;
|
||||
|
||||
// Rebalance target FHS reselect time limit( min ), default 10 min, use transfer total size calc.
|
||||
m_nFhsReselectionTimeLimit = 10;
|
||||
|
||||
// Content 복제 및 Cache 관련 전송시 Hash Check Level
|
||||
// 0:Not check (default), 1:Partitial hash, 2:Full Hash
|
||||
m_nHashCheckLevel = 0;
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - default 는 큰 파일 순서대로 이동 처리 시도...
|
||||
m_nContentSelectMode = SELECT_MODE_SIZE;
|
||||
memset( &m_timeContentSelectModeChange, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 default 값 추가
|
||||
m_dTargetFhsMaxDiskBusyLimit = DEFAULT_TARGET_FHS_MAX_DISK_BUSY_LIMIT;
|
||||
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProcessConfig::~CProcessConfig()
|
||||
{
|
||||
// 객체 소멸시...
|
||||
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
|
||||
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
|
||||
if( CProcessConfig::GetInstance() == this )
|
||||
{
|
||||
CProcessConfig::m_pInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CProcessConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
|
||||
{
|
||||
|
||||
// 1. 임시 처리용 Instance 를 생성한다.
|
||||
CProcessConfig * pTempInstance = new CProcessConfig();
|
||||
|
||||
// 2. 전달 받은 Config 정보를 임시 Instance 로 로딩한다.
|
||||
// - 만약 로딩이 실패할 경우... 임시 Instance 객체를 삭제 처리한다.
|
||||
if( pTempInstance->Load( strProgramName, strConfFileName, strErrorMessage ) == false )
|
||||
{
|
||||
delete pTempInstance;
|
||||
pTempInstance = NULL;
|
||||
|
||||
return false; // 생성 실패 사유는 Error String 관련 함수를 사용하여 확인.
|
||||
}
|
||||
|
||||
// 3. 전달받은 Conf 정보 로딩에 성공한 경우...
|
||||
// - 기존 Instance 객체가 존재하는 경우.. 교체 처리..
|
||||
// - 기존 Instance 객체가 없는 경우는 신규 Instance 사용토록 처리.
|
||||
if( CProcessConfig::GetInstance() == NULL )
|
||||
{
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Instance replace...
|
||||
CProcessConfig * pPreviosInstance = CProcessConfig::m_pInstance;
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
|
||||
delete pPreviosInstance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CProcessConfig * CProcessConfig::GetInstance()
|
||||
{
|
||||
return CProcessConfig::m_pInstance;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
|
||||
{
|
||||
// 변수 유효성 확인
|
||||
if( strProgramName.size() <= 0 )
|
||||
{
|
||||
strErrorMessage = "Config Program Name[" + strProgramName + "] not valid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Config class (lib 하위) 를 이용하여 config 파일 상의 정보를 로딩 처리
|
||||
Config conf;
|
||||
|
||||
if( conf.Open( strConfFileName ) == false )
|
||||
{
|
||||
strErrorMessage = "Config file[" + strConfFileName + "] open failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Config 모듈에 로딩된 정보 중 필요한 정보만 가져온다...
|
||||
// - 만약 오류가 발생할 경우... 해당 내역은 Error String 에 저장 처리...
|
||||
|
||||
std::string strValue;
|
||||
//std::vector< std::string > vecValue;
|
||||
|
||||
|
||||
// DEFAULT_LOG_DIR
|
||||
if( GetConfigValue( conf, strProgramName, "DEFAULT_LOG_DIR", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strLogPath = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// LOG_LEVEL
|
||||
if( GetConfigValue( conf, strProgramName, "LOG_LEVEL", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_nLogLevel = atoi(strValue.c_str());
|
||||
if( m_nLogLevel < 0 || m_nLogLevel > MAX_LOG_LEVEL )
|
||||
{
|
||||
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCID
|
||||
if( GetConfigValue( conf, strProgramName, "RC_ID", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcId = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB IP
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_IP", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbIp = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB PORT
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_PORT", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_nRcdbPort = atoi( strValue.c_str() );
|
||||
|
||||
if( m_nRcdbPort <= 0 || m_nRcdbPort > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB DB NAME
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbName = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB ACCOUNT
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbAcct = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// RCDB ACCOUNT PASSWORD
|
||||
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcdbAcctPw = strValue;
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
|
||||
// MAX_QUEUE_COUNT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "MAX_QUEUE_COUNT", strValue, strErrorMessage) == true)
|
||||
{
|
||||
m_nMaxQueueSize = atoi(strValue.c_str());
|
||||
|
||||
// Queue 역시 1~ 65535 까지만 설정할 수 있도록 한다.
|
||||
if( m_nMaxQueueSize <= 0 || m_nMaxQueueSize > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [MAX_QUEUE_COUNT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// FHS_TRANSFER_DAEMON_PORT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "FHS_TRANSFER_DAEMON_PORT", strValue, strErrorMessage) == true )
|
||||
{
|
||||
m_nFhsTransferDaemonPort = atoi(strValue.c_str());
|
||||
|
||||
if( m_nFhsTransferDaemonPort <= 0 || m_nFhsTransferDaemonPort > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [FHS_TRANSFER_DAEMON_PORT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// FHS_RESELECTION_TIME_LIMIT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue( conf, strProgramName, "FHS_RESELECTION_TIME_LIMIT", strValue, strErrorMessage ) == true )
|
||||
{
|
||||
m_nFhsReselectionTimeLimit = atoi( strValue.c_str() );
|
||||
|
||||
if( m_nFhsReselectionTimeLimit < 5 || m_nFhsReselectionTimeLimit > 1440 )
|
||||
{
|
||||
strErrorMessage = "Config [FHS_RESELECTION_TIME_LIMIT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// FILE_HASH_CHECK_LEVEL
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "FILE_HASH_CHECK_LEVEL", strValue, strErrorMessage) == true )
|
||||
{
|
||||
m_nHashCheckLevel = atoi(strValue.c_str());
|
||||
|
||||
if( m_nHashCheckLevel < 0 || m_nHashCheckLevel > 2 )
|
||||
{
|
||||
strErrorMessage = "Config [FILE_HASH_CHECK_LEVEL] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// TARGET_FHS_MAX_DISK_BUSY_LIMIT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
// - conf 설정은 정수, 저장 변수는 double type 이므로.. 설정값 존재지 형 변환 처리 필요.
|
||||
if( GetConfigValue( conf, strProgramName, "TARGET_FHS_MAX_DISK_BUSY_LIMIT", strValue, strErrorMessage ) == true )
|
||||
{
|
||||
int value = atoi( strValue.c_str() );
|
||||
|
||||
// 80% 이상값 설정시 오류로 표시 처리
|
||||
if( value < 0 || value > 80 )
|
||||
{
|
||||
strErrorMessage = "Config [TARGET_FHS_MAX_DISK_BUSY_LIMIT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
// double type 으로 변환 처리하여 저장.
|
||||
m_dTargetFhsMaxDiskBusyLimit = (double)( 1.0 * value );
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
|
||||
// conf 정보 로딩이 모두 정상적으로 완료된 경우... 마지막으로 전달받은 config 명, section 정보를 저장한다.
|
||||
m_strConfigFileName = strConfFileName;
|
||||
m_strProgramName = strProgramName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage )
|
||||
{
|
||||
// conf get value
|
||||
if( conf.GetConfig( strProgramName, strKey, strValue) == false )
|
||||
{
|
||||
if( conf.GetConfig( "COMMON", strKey, strValue) == false )
|
||||
{
|
||||
strErrorMessage = "Config [" + strKey + "] value is not exist";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// value check.
|
||||
if( strValue.empty() == true )
|
||||
{
|
||||
strErrorMessage = "Config [" + strKey + "] value is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CProcessConfig::SetContentSelectMode( ContentSelectMode mode )
|
||||
{
|
||||
// 기존 mode 와 비교하여 서로 다른 mode 이면...
|
||||
if( m_nContentSelectMode != mode )
|
||||
{
|
||||
m_nContentSelectMode = mode;
|
||||
|
||||
// mode 변경 시간 정보 update.
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeContentSelectModeChange );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CProcessConfig::SetContentSelectModeToDefault()
|
||||
{
|
||||
// mode 변경 시간이 ... 지정된 시간을 초과한 경우.. default mode 로 변경 처리한다.
|
||||
if( m_nContentSelectMode != SELECT_MODE_SIZE )
|
||||
{
|
||||
struct timespec timeNow;
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
|
||||
long timeElapsed = (long)( timeNow.tv_sec ) - (long)( m_timeContentSelectModeChange.tv_sec );
|
||||
if( timeElapsed > CONTENT_SELECT_MODE_TO_DEFAULT_TIMEOUT )
|
||||
{
|
||||
m_nContentSelectMode = SELECT_MODE_SIZE;
|
||||
|
||||
_LOG( LINF, "content select mode change to default: Big Size Order" );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/****************************************************************************
|
||||
rc_balanced config processing module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/25
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __PROCESS_CONFIG_H__
|
||||
#define __PROCESS_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
typedef enum TypeContentSelectMode {
|
||||
SELECT_MODE_SIZE = 0, // 큰 파일 우선 이동 처리
|
||||
SELECT_MODE_RANDOM // 이동 대상 content 를 random 으로 선택
|
||||
|
||||
} ContentSelectMode;
|
||||
|
||||
#define CONTENT_SELECT_MODE_TO_DEFAULT_TIMEOUT 10800 // content mode 가 default 가 아닌 경우... 다시 default 로 전환시킬 timeout 값 (sec)
|
||||
|
||||
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
|
||||
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
|
||||
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
|
||||
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
|
||||
class CProcessConfig
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
|
||||
CProcessConfig();
|
||||
|
||||
// 소멸자
|
||||
virtual ~CProcessConfig();
|
||||
|
||||
static CProcessConfig * m_pInstance;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 객체 초기화 처리용 Function.
|
||||
// 본 함수 호출시 Instance 생성 후 입력 받은 Config 파일 load.
|
||||
// 따라서 본 함수를 호출하지 않을 경우.. GetInstance() 함수는 NULL 을 반환
|
||||
// 본 함수를 객체가 생성된 상태에서 재 호출할 경우
|
||||
// - 주어진 Conf 파일 정보가 정확하다면.. conf 정보를 다시 load 함.
|
||||
// - 주어진 conf 파일 정보가 부정확하다면... 이전 conf 를 계속 사용함.
|
||||
static bool Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
|
||||
|
||||
// Singleton 객체 접근 메소드
|
||||
static CProcessConfig * GetInstance();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 전달받은 conf 파일에서 해당 config 정보를 로드하여 멤버 변수에 저장한다.
|
||||
// 만약 반환값이 false 인 경우 해당 오류 관련 내역은 strErrorMessage 변수에 저장된다.
|
||||
bool Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
|
||||
|
||||
|
||||
// Config 객체로 부터 지정된 strKey 정보를 가져와 유효성 여부를 판단.
|
||||
// 반환값이 false 인 경우 strErrorMessage 상에 오류 내역이 저장된다.
|
||||
bool GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 객체 초기화시 전달받은 정보
|
||||
std::string m_strConfigFileName; // 설정 파일 정보
|
||||
std::string m_strProgramName; // 프로그램 명 (conf 상에서 해당 프로그램의 설정 정보를 가져올 때 사용)
|
||||
|
||||
|
||||
// 기본 설정 정보.
|
||||
std::string m_strLogPath; // 로그 저장 Path (/user/service/logs)
|
||||
int m_nLogLevel; // 로그 기록 Level
|
||||
|
||||
std::string m_strRcId; // RC ID
|
||||
|
||||
std::string m_strRcdbIp; // RCDB IP
|
||||
unsigned int m_nRcdbPort; // RCDB Port
|
||||
std::string m_strRcdbName; // RCDB DB Name
|
||||
std::string m_strRcdbAcct; // RCDB 접근 계정
|
||||
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
|
||||
|
||||
|
||||
// 프로세스 설정 정보
|
||||
|
||||
unsigned int m_nMaxQueueSize; // Max Queue Size ( default: 4096 )
|
||||
|
||||
unsigned int m_nFhsTransferDaemonPort; // FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
|
||||
// Rebalance target FHS reselect time limit (minutes), default 30 min, use transfer total size calc, max 1440
|
||||
unsigned int m_nFhsReselectionTimeLimit;
|
||||
|
||||
unsigned int m_nHashCheckLevel; // Content 복제 및 Cache 관련 전송시 Hash Check Level 설정.
|
||||
// 0:Not check (default), 1:Partitial hash, 2:Full Hash
|
||||
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 멤버 변수 추가
|
||||
double m_dTargetFhsMaxDiskBusyLimit;
|
||||
|
||||
|
||||
// 프로세스 내부 동작 관련 변수
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
ContentSelectMode m_nContentSelectMode; // move content select mode, 상세내역은 ContentSelectMode 참고
|
||||
struct timespec m_timeContentSelectModeChange; // move content select mode 가 변경된 최종 시간.
|
||||
|
||||
|
||||
public: // 멤버 변수에 대한 Get 함수 선언 및 정의
|
||||
|
||||
const char * GetConfigFileName() { return m_strConfigFileName.c_str(); }
|
||||
const char * GetProgramName() { return m_strProgramName.c_str(); }
|
||||
|
||||
const char * GetLogPath() { return m_strLogPath.c_str(); }
|
||||
int GetLogLevel() { return m_nLogLevel; }
|
||||
|
||||
const char * GetRcId() { return m_strRcId.c_str(); }
|
||||
|
||||
const char * GetRcdbIp() { return m_strRcdbIp.c_str(); }
|
||||
unsigned int GetRcdbPort() { return m_nRcdbPort; }
|
||||
const char * GetRcdbName() { return m_strRcdbName.c_str(); }
|
||||
const char * GetRcdbAcct() { return m_strRcdbAcct.c_str(); }
|
||||
const char * GetRcdbAcctPw() { return m_strRcdbAcctPw.c_str(); }
|
||||
|
||||
unsigned int GetMaxQueueSize() { return m_nMaxQueueSize; }
|
||||
unsigned int GetFhsTransferDaemonPort() { return m_nFhsTransferDaemonPort; }
|
||||
unsigned int GetFhsReselectionTimeLimit() { return m_nFhsReselectionTimeLimit; }
|
||||
unsigned int GetHashCheckLevel() { return m_nHashCheckLevel; }
|
||||
|
||||
double GetTargetFhsMaxDiskBusyLimit() { return m_dTargetFhsMaxDiskBusyLimit; }
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - GET/SET 함수 추가
|
||||
ContentSelectMode GetContentSelectMode() { return m_nContentSelectMode; }
|
||||
void SetContentSelectMode( ContentSelectMode mode );
|
||||
bool SetContentSelectModeToDefault();
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_CONFIG_H__ */
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
#include "ProcessMonitoringThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessRename.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
// 정보 갱신 주기 (sec)
|
||||
// - rebalance 작업의 console 상태 정보는 2 sec 단위로 업데이트 한다.
|
||||
#define INTERVAL_UPDATE_INFO 2
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessMonitoringThread::CProcessMonitoringThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CCopyControlThread * const pCopyControlThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby)
|
||||
, m_pQueueUpdateStandby( pQueueUpdateStandby)
|
||||
, m_pQueueDeleteStandby( pQueueDeleteStandby )
|
||||
, m_pCopyControlThread( pCopyControlThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_mapUnlinkInfo.clear();
|
||||
pthread_mutex_init( &m_mutexUnlinkInfo, NULL );
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProcessMonitoringThread::~CProcessMonitoringThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
pthread_mutex_destroy( &m_mutexUnlinkInfo );
|
||||
m_mapUnlinkInfo.clear();
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
if( m_pCopyControlThread != NULL )
|
||||
m_pCopyControlThread = NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
bool CProcessMonitoringThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL || m_pQueueUpdateStandby == NULL || m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "ProcessMonitoringThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_pCopyControlThread == NULL )
|
||||
{
|
||||
LOG( LERR, "ProcessMonitoringThread: copy thread object pointer not valid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CProcessMonitoringThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "ProcessMonitoringThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::UpdateTitle( void )
|
||||
{
|
||||
char szTemp[100] = { 0 };
|
||||
|
||||
// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
|
||||
// 수정할 경우 PrintUsage(), Execute() 의 내역도 수정해야 함.
|
||||
sprintf( szTemp, "%s: S%u T%d U%u D%u"
|
||||
, PROG_NAME
|
||||
, m_pQueueMoveStandby->GetSize()
|
||||
, m_pCopyControlThread->GetCopyThreadCount()
|
||||
, m_pQueueUpdateStandby->GetSize()
|
||||
, m_pQueueDeleteStandby->GetSize()
|
||||
);
|
||||
|
||||
set_ps_display( szTemp, false );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::UpdateDeletedInfo( CRebalanceData & data )
|
||||
{
|
||||
// CPhysicalDeleteThread 에서
|
||||
// 정상적으로 삭제 처리한 content 에 대한 FHS, 삭제 크기 정보를 전달받는다.
|
||||
// 전달받은 정보는 내부적으로 저장했다가 주기적으로 로깅 처리한다.
|
||||
std::map<std::string, struct FhsUnlinkInfo>::iterator it;
|
||||
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
it = m_mapUnlinkInfo.find( data.m_szOriginHost );
|
||||
|
||||
if( it != m_mapUnlinkInfo.end() )
|
||||
{
|
||||
// 이미 해당 FHS 가 존재하는 경우...
|
||||
// - 해당 정보를 추가로 sum 처리한다...
|
||||
it->second.hour_count += 1;
|
||||
it->second.hour_sum += data.m_uOriginContentLength;
|
||||
it->second.day_count += 1;
|
||||
it->second.day_sum += data.m_uOriginContentLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 해당 FHS 정보가 없는 경우...
|
||||
// - 신규로 추가한다.
|
||||
// - map 특성상 key 값에 의한 자동 sort 처리됨.
|
||||
struct FhsUnlinkInfo fhs;
|
||||
|
||||
fhs.hour_count = 1;
|
||||
fhs.hour_sum = data.m_uOriginContentLength;
|
||||
fhs.day_count = 1;
|
||||
fhs.day_sum = data.m_uOriginContentLength;
|
||||
|
||||
m_mapUnlinkInfo[ data.m_szOriginHost ] = fhs;
|
||||
}
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
_LOG( LDBG, "ProcessMonitoringThread: deleted info update. [%s %s %llu]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
|
||||
// Thread loop 기본 함수
|
||||
void * CProcessMonitoringThread::threadFunc( void * arg )
|
||||
{
|
||||
CProcessMonitoringThread * pObject = reinterpret_cast<CProcessMonitoringThread *>( arg );
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( INTERVAL_UPDATE_INFO );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// 실제 정보 update 처리 함수
|
||||
void CProcessMonitoringThread::Execute()
|
||||
{
|
||||
char szTemp[100] = { 0 };
|
||||
|
||||
unsigned int nCountMoveStandby = m_pQueueMoveStandby->GetSize();
|
||||
unsigned int nCountUpdateStandby = m_pQueueUpdateStandby->GetSize();
|
||||
unsigned int nCountDeleteStandby = m_pQueueDeleteStandby->GetSize();
|
||||
|
||||
int nCountContentTransferThread = m_pCopyControlThread->GetCopyThreadCount();
|
||||
|
||||
int nCountMaxContentTransferThread = 0;
|
||||
|
||||
// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
|
||||
// 수정할 경우 PrintUsage(),UpdateTitle() 의 내역도 수정해야 함.
|
||||
sprintf( szTemp, "%s: S%u T%d U%u D%u"
|
||||
, PROG_NAME
|
||||
, nCountMoveStandby
|
||||
, nCountContentTransferThread
|
||||
, nCountUpdateStandby
|
||||
, nCountDeleteStandby );
|
||||
|
||||
set_ps_display( szTemp, false );
|
||||
|
||||
if( nCountMoveStandby != 0
|
||||
|| nCountContentTransferThread != 0
|
||||
|| nCountUpdateStandby != 0
|
||||
|| nCountDeleteStandby != 0 )
|
||||
{
|
||||
nCountMaxContentTransferThread = m_pCopyControlThread->GetMaxCopyThreadCount();
|
||||
|
||||
_LOG( LINF, "[MONITOR] Move standby: %4u, Transfer thread: %d/%d, Update standby: %4u, Unlink standby: %4u"
|
||||
, nCountMoveStandby
|
||||
, nCountContentTransferThread, nCountMaxContentTransferThread
|
||||
, nCountUpdateStandby
|
||||
, nCountDeleteStandby );
|
||||
}
|
||||
|
||||
// 각 FHS 별 삭제한 통계 정보를 logging 및 초기화 처리수행.
|
||||
printUnlinkInfo();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::printUnlinkInfo()
|
||||
{
|
||||
if( m_mapUnlinkInfo.empty() == true )
|
||||
return;
|
||||
|
||||
// 시간 check
|
||||
// - 시간 단위로 FHS 별 삭제한 정보를 로깅하고 초기화.
|
||||
// - 일 단위 FHS 별 삭제한 정보는 매일 10시에 로깅 및 초기화 되도록 한다.
|
||||
time_t currentTime = time( NULL );
|
||||
long currentSec = currentTime % 3600; // 1시간 = 3600 sec 로 나누어서 나머지가 현재 시간의 sec 를 나타냄
|
||||
|
||||
if( currentSec == 0 || currentSec == 1 ) // 각 시간 정각 또는 정각+1 sec
|
||||
{
|
||||
// total 정보 출력을 위한 변수
|
||||
unsigned long long uTotalFhsCount = 0;
|
||||
unsigned long long uTotalContentCount = 0;
|
||||
unsigned long long uTotalContentSum = 0;
|
||||
|
||||
std::map<std::string, struct FhsUnlinkInfo>::iterator it;;
|
||||
struct tm currentTm;
|
||||
memset( ¤tTm, 0x00, sizeof( struct tm ) );
|
||||
|
||||
localtime_r( ¤tTime, ¤tTm );
|
||||
|
||||
if( currentTm.tm_hour == 10 )
|
||||
{
|
||||
// 오전 9시 정각에는 full logging.
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
|
||||
for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
|
||||
{
|
||||
uTotalFhsCount += 1;
|
||||
uTotalContentCount += it->second.day_count;
|
||||
uTotalContentSum += it->second.day_sum;
|
||||
|
||||
// FHS 별 확보한 용량 통계 정보 출력
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance day report:: %s , count:%5lu , size:%5llu GiB."
|
||||
, it->first.c_str()
|
||||
, it->second.day_count, (it->second.day_sum)/1024/1024/1024 );
|
||||
}
|
||||
// 통계 정보 초기화
|
||||
m_mapUnlinkInfo.clear();
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
// total report logging
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance total day report:: fhs: %llu , content: %llu , total_size: %llu GiB."
|
||||
, uTotalFhsCount
|
||||
, uTotalContentCount
|
||||
, uTotalContentSum / 1024 / 1024 / 1024 );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 나머지는 시간당 삭제 처리한 정보 로깅
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
|
||||
for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
|
||||
{
|
||||
// 해당 시간에 작업하지 않은 FHS 는 제외
|
||||
if( it->second.hour_count > 0 )
|
||||
{
|
||||
uTotalFhsCount += 1;
|
||||
uTotalContentCount += it->second.hour_count;
|
||||
uTotalContentSum += it->second.hour_sum;
|
||||
|
||||
// FHS 별 확보한 용량 통계 정보 출력
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance hour report:: %s , count:%5lu , size:%5llu GiB."
|
||||
, it->first.c_str()
|
||||
, it->second.hour_count, (it->second.hour_sum) / 1024 / 1024 / 1024 );
|
||||
|
||||
// 각 장비 시간 단위 통계 정보 초기화
|
||||
it->second.hour_count = 0;
|
||||
it->second.hour_sum = 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
|
||||
// total report logging
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance total hour report:: fhs: %llu , content: %llu , total_size: %llu GiB."
|
||||
, uTotalFhsCount
|
||||
, uTotalContentCount
|
||||
, uTotalContentSum / 1024 / 1024 / 1024 );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
rc_balanced process monitoring thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/07
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __PROCESS_MONITORING_THREAD_H__
|
||||
#define __PROCESS_MONITORING_THREAD_H__
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
|
||||
// 각 FHS 별 unlink 처리한 정보 저장을 위한 구조체
|
||||
// map 을 통해서 관리하기 때문에 각 장비 hostname 을 정보에 포함시키지 않는다.
|
||||
struct FhsUnlinkInfo {
|
||||
|
||||
unsigned long long hour_sum; // 시간당 삭제 sum
|
||||
unsigned long hour_count; // 시간당 삭제 count
|
||||
|
||||
unsigned long long day_sum; // 일일 삭제 sum
|
||||
unsigned long day_count; // 일일 삭제 count
|
||||
};
|
||||
|
||||
|
||||
|
||||
// CProcessMonitoringThread class
|
||||
// - 프로세스 내부의 각 작업 queue count 정보를 주기적으로 console 상에 업데이트 처리
|
||||
// - 주기적으로 rebalance 처리 상황을 로깅
|
||||
class CProcessMonitoringThread
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CProcessMonitoringThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CCopyControlThread * const pCopyControlThread
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CProcessMonitoringThread();
|
||||
|
||||
|
||||
// Thread 를 생성하여 상태정보를 주기적으로 갱신, 로깅 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
// 현재 프로세스 표시 정보 직접 업데이트
|
||||
void UpdateTitle( void );
|
||||
|
||||
// PhysicalDeleteThread 모듈로 부터 정상적으로 삭제한 FHS, content 정보를 전달받기 위한 함수
|
||||
// - 전달받은 정보는 내부적으로 저장했다가 주기적으로 logging 처리
|
||||
void UpdateDeletedInfo( CRebalanceData & data );
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread Handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Move 처리할 대상을 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// copy control thread 객체에 대한 포인터
|
||||
CCopyControlThread * m_pCopyControlThread;
|
||||
|
||||
// unlink 처리한 FHS 별 통계 정보 저장을 위한 객체
|
||||
std::map<std::string, struct FhsUnlinkInfo> m_mapUnlinkInfo;
|
||||
pthread_mutex_t m_mutexUnlinkInfo;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 상태 정보 업데이트를 수행하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// FHS 별 삭제 처리한 통계 정보를 로깅 및 초기화 처리한다.
|
||||
void printUnlinkInfo( void );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_MONITORING_THREAD_H__ */
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "RcdbInfo.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
// 생성자..
|
||||
CRcdbInfo::CRcdbInfo()
|
||||
{
|
||||
}
|
||||
|
||||
// 소멸자..
|
||||
CRcdbInfo::~CRcdbInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
|
||||
void CRcdbInfo::Load()
|
||||
{
|
||||
// CProcessConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
|
||||
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
|
||||
|
||||
// RCDB IP
|
||||
m_strRcdbIp = CProcessConfig::GetInstance()->GetRcdbIp();
|
||||
|
||||
// RCDB Port
|
||||
m_nRcdbPort = CProcessConfig::GetInstance()->GetRcdbPort();
|
||||
|
||||
// RCDB DB Name
|
||||
m_strRcdbName = CProcessConfig::GetInstance()->GetRcdbName();
|
||||
|
||||
// RCDB 접근 계정
|
||||
m_strRcdbAcct = CProcessConfig::GetInstance()->GetRcdbAcct();
|
||||
|
||||
// RCDB 접근 Password.
|
||||
m_strRcdbAcctPw = CProcessConfig::GetInstance()->GetRcdbAcctPw();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
RCDB 접속 정보 저장 처리 Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2013/11/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __RCDB_INFO_H__
|
||||
#define __RCDB_INFO_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
/// @brief CRcdbInfo
|
||||
/// 본 클래스는 conf 파일에 저장된 RCDB 접속 관련 정보를 저장하기 위한 공용 Class...
|
||||
/// 본 클래스를 사용하지 않고... CServiceConfig class 의 Get 함수를 이용하여 RCDB 접속 정보를 처리해도 된다..
|
||||
/// 하지만... RCDB 접속 처리 Class 함수의 string type 변수 사용으로 인해... 여러 모듈에서 반복적인 동일 코드 처리 작업을 수행해야 하므로...
|
||||
/// 본 Class 를 통해 코드를 단순화 시킬 목적으로 사용한다.
|
||||
class CRcdbInfo
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성 및 소멸자.
|
||||
CRcdbInfo();
|
||||
~CRcdbInfo();
|
||||
|
||||
// CServiceConfig class 에 저장된 RCDB 접속 정보를 멤버 변수에 저장처리한다.
|
||||
void Load( void );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 멤버 변수들은 DB 접속을 처리하는 객체에서 직접 Access 해서 사용할 수 있도록 public 으로 구성. ( 편의성 제공 목적)
|
||||
std::string m_strRcdbIp; // RCDB IP
|
||||
unsigned int m_nRcdbPort; // RCDB Port
|
||||
std::string m_strRcdbName; // RCDB DB Name
|
||||
std::string m_strRcdbAcct; // RCDB 접근 계정
|
||||
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
|
||||
|
||||
};
|
||||
|
||||
#endif /* __RCDB_INFO_H__ */
|
||||
@@ -0,0 +1,244 @@
|
||||
#include "RcdbUpdateThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Database.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 15
|
||||
#define SLEEP_TIME_MAX 30
|
||||
|
||||
|
||||
CRcdbUpdateThread::CRcdbUpdateThread( CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby )
|
||||
: m_pQueueUpdateStandby( pQueueUpdateStandby ), m_pQueueDeleteStandby( pQueueDeleteStandby )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
}
|
||||
|
||||
CRcdbUpdateThread::~CRcdbUpdateThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CRcdbUpdateThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueUpdateStandby == NULL || m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CRcdbUpdateThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CRcdbUpdateThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CRcdbUpdateThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CRcdbUpdateThread::threadFunc( void * arg )
|
||||
{
|
||||
CRcdbUpdateThread * pObject = reinterpret_cast<CRcdbUpdateThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CRcdbUpdateThread::Execute()
|
||||
{
|
||||
// RCDB update 대상 content 가 없다면.. MAX sleep 처리한다.
|
||||
if( m_pQueueUpdateStandby->GetSize() == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] standby queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// update 대기 count 가 delete 대기 count 보다 많은 경우
|
||||
// delete 대기 queue 에 push 실패 발생할 가능성 있으므로.. push 실패할 상황이면.. 차라리 sleep 한다.
|
||||
|
||||
unsigned int uUpdateStandbyCount = m_pQueueUpdateStandby->GetSize();
|
||||
unsigned int uDeletePushEnableCount = m_pQueueDeleteStandby->GetMaxSize() - m_pQueueDeleteStandby->GetSize();
|
||||
if( uUpdateStandbyCount > uDeletePushEnableCount )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] delete queue not enough (%u %u). max sleep %u sec."
|
||||
, uUpdateStandbyCount, uDeletePushEnableCount, m_uSleep );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
|
||||
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
|
||||
{
|
||||
// 연결 실패시...
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
LOG( LERR, "[UPDATE ] RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[UPDATE ] start." );
|
||||
|
||||
CRebalanceData data;
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// RCDB 연결 성공시
|
||||
// - queue pop 처리하여... RCDB 업데이트 처리.
|
||||
while( m_pQueueUpdateStandby->GetSize() > 0 )
|
||||
{
|
||||
data = m_pQueueUpdateStandby->Front();
|
||||
m_pQueueUpdateStandby->Pop();
|
||||
|
||||
// COPY 처리가 성공한 content 만 RCDB 업데이트 수행
|
||||
// - 그 외에는 실패 상황이므로.. RCDB 업데이트 하면 안됨.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// Query 조립
|
||||
// - deleted_yn = 'N' : 이미 삭제된 경우... COPY 처리한 파일을 삭제하기 위함
|
||||
// - get_content_length = %llu : 원본이 변경된 경우.. copy 처리한 파일을 삭제처리하기 위함
|
||||
// - uri 미사용 : uri 는 특수문자 이슈 때문에 사용하지 않고 대신 resource_id 정보를 사용함.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "UPDATE t_meta_%s "
|
||||
"SET host_name = '%s', filename_hash = '%s' "
|
||||
"WHERE resource_id = %llu AND host_name = '%s' AND filename_hash = '%s' "
|
||||
" AND get_content_length = %llu AND deleted_yn = 'N' AND resource_type = 0 ;"
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_szMoveHost.c_str(), data.m_szMoveFileNameHash.c_str()
|
||||
, data.m_uResourceId, data.m_szOriginHost.c_str(), data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
|
||||
// update query 수행
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.GetCmdTuples();
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] query result. [%d] [%s]", nResult, szQuery );
|
||||
|
||||
if( nResult == 1 ) // 적용된 row count = 1 : 정상
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
_LOG( LINF, "[UPDATE ] content meta update ok.[%s id:%llu size:%llu %s]->[%s size:%llu %s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_uResourceId
|
||||
, data.m_uOriginContentLength, data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_szMoveHost.c_str(), data.m_uMoveContentLength, data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
else if( nResult == 0 ) // 적용된 row count = 0 : 이미 삭제 또는 원본 변경된 경우... copy content 삭제하도록 설정
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
// copy content 삭제하도록 data 객체 정보 변경 처리
|
||||
data.m_uMoveResult = MOVE_ERROR_UPDATE;
|
||||
|
||||
LOG( LERR, "[UPDATE ] content meta update failed. origin modified. [%s id:%llu size:%llu %s][%s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_uResourceId
|
||||
, data.m_uOriginContentLength, data.m_szOriginFileNameHash.c_str(), data.m_szURI.c_str() );
|
||||
}
|
||||
else // 그외 : resource_id 조건으로 query 실패 가능성은 없음.. DB 장애로 인지해야 할 듯.
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
// 어떤 상황인지 정확히 알 수 없으므로.. 원본, copy 본 모두 유지시킨다.
|
||||
data.m_uMoveResult = MOVE_ERROR_CREATE;
|
||||
|
||||
// 운영자가 판단하도록 로깅 처리한다.
|
||||
LOG( LERR, "[UPDATE ] content meta update result error. op check.[%d][id:%llu %s]"
|
||||
, nResult
|
||||
, data.m_uResourceId, data.m_szURI.c_str() );
|
||||
|
||||
// 더 이상 작업을 수행하지 않고 MAX sleep 처리한다.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// delete standby queue 에 content 를 push 처리한다.
|
||||
// - 앞에서 delete queue 에 충분한 여유공간 존재하는지 check 한 후 push 하므로...
|
||||
// - push 실패는 .. 아마도... 공간 부족이 아닌 다른 문제일 가능성 존재..
|
||||
// - 우선 1번 재시도해서도 실패하면 버린다.
|
||||
if( m_pQueueDeleteStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LWAR, "[UPDATE ] delete standby queue 1st push fail.");
|
||||
sleep( 1 );
|
||||
|
||||
if( m_pQueueDeleteStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LERR, "[UPDATE ] delete standby queue push all fail. throw.");
|
||||
|
||||
// 다음번 push 작업도 실패 가능성 존재하므로....
|
||||
// 더 이상 작업하지 않고.. loop 종료하고.. 다음에 작업 진행한다.
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // while
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content RCDB update thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __RCDB_UPDATE_THREAD_H__
|
||||
#define __RCDB_UPDATE_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
|
||||
|
||||
// CRcdbUpdateThread class
|
||||
// - RCDB update queue 에 들어 있는 content 를 pop 하여...
|
||||
// - RCDB 에 UPDATE 처리를 수행한 후
|
||||
// - 최종 해당 Content 를 unlink 처리를 위한 대기 queue 에 push 처리를 수행
|
||||
class CRcdbUpdateThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CRcdbUpdateThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CRcdbUpdateThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __RCDB_UPDATE_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "RebalanceData.h"
|
||||
|
||||
|
||||
|
||||
// 생성자
|
||||
CRebalanceData::CRebalanceData()
|
||||
{
|
||||
// 멤버 변수 초기화
|
||||
m_uOriginContentLength = 0;
|
||||
m_uMoveContentLength = 0;
|
||||
|
||||
m_uMoveResult = MOVE_OK; // Move 처리 결과는 최초에는 정상 상태로 설정 처리
|
||||
|
||||
m_uDeleteFailCount = 0;
|
||||
m_timeLastDeleteFail = 0;
|
||||
|
||||
}
|
||||
|
||||
CRebalanceData::~CRebalanceData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::string CRebalanceData::GetQueryURI()
|
||||
{
|
||||
// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
|
||||
std::string szUri = ReplaceString( m_szURI, "'", "''" );
|
||||
|
||||
return szUri;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
std::string CRebalanceData::ReplaceString( const std::string & source, const std::string search, const std::string replacement )
|
||||
{
|
||||
std::string r = source;
|
||||
|
||||
std::string::size_type pos = 0;
|
||||
|
||||
while( (pos = r.find( search, pos )) != std::string::npos )
|
||||
{
|
||||
r.replace( pos, search.size(), replacement );
|
||||
pos = pos + replacement.size();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
rc_balanced data object define
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/04
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __REBALANCE_DATA_H__
|
||||
#define __REBALANCE_DATA_H__
|
||||
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
// CRebalanceData 객체의 Move 처리 결과 멤버변수인 m_uMoveResult 에 저장될 값 정의
|
||||
enum MoveResults {
|
||||
MOVE_OK = 0, // 성공 -> Origin content 삭제
|
||||
MOVE_ERROR_CREATE = 1, // 물리 파일 생성 실패 (origin 접근 실패, target FHS 선정 실패등) -> 추가 action 불필요
|
||||
MOVE_ERROR_HASHCHECK = 2, // 물리 파일 생성했으나 Hash check mismatch -> 생성된 파일 삭제 처리 필요
|
||||
MOVE_ERROR_UPDATE = 3 // 물리 파일 생성했으나 RCDB update 실패 -> 생성된 파일 삭제 처리 필요
|
||||
|
||||
};
|
||||
|
||||
|
||||
// rebalnce 처리 object data 에 대한 세부 정보를 저장하기 위한 data class
|
||||
class CRebalanceData
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CRebalanceData();
|
||||
|
||||
// 소멸자
|
||||
~CRebalanceData();
|
||||
|
||||
|
||||
// URI 정보는 특수문자 포함될 수 있어.. Query 에서 사용시 변환 처리 함수
|
||||
std::string GetQueryURI( void );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 공통
|
||||
|
||||
unsigned long long m_uResourceId; // resouce_id 정보 (고유 key)
|
||||
std::string m_szURI; // URI 정보 (특수 문자 포함관련 query 에서 사용시 변환 필요)
|
||||
std::string m_szServiceSeq; // 서비스 sequence number
|
||||
|
||||
|
||||
// 원본 Content 정보
|
||||
|
||||
std::string m_szOriginHost; // origin hostname 정보
|
||||
std::string m_szOriginFileNameHash; // origin filename_hash
|
||||
unsigned long long m_uOriginContentLength; // origin get_content_length
|
||||
|
||||
// Move 처리 결과
|
||||
// 세부 사항은 enum MoveResults 선언 참고
|
||||
enum MoveResults m_uMoveResult;
|
||||
|
||||
|
||||
// Move Content 정보
|
||||
|
||||
std::string m_szMoveHost; // move hostname 정보
|
||||
std::string m_szMoveFileNameHash; // move filename_hash
|
||||
unsigned long long m_uMoveContentLength; // move get_content_length
|
||||
|
||||
// Delete 처리 관련 정보
|
||||
|
||||
unsigned int m_uDeleteFailCount; // delete 처리 관련 fail 횟수
|
||||
time_t m_timeLastDeleteFail; // delete 처리 관련 마지막 실패 시각
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// uri 상의 특수 문자 변경을 위한 함수
|
||||
std::string ReplaceString( const std::string& source, const std::string search, const std::string replacement );
|
||||
};
|
||||
|
||||
|
||||
#endif /* __REBALANCE_DATA_H__ */
|
||||
|
||||
@@ -0,0 +1,716 @@
|
||||
#include "ShmInfoThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Common.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "../rc_mond/ShmDefine.h"
|
||||
#include "../rc_mond/ShmControl.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm> // vector sort 사용 목적
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 20
|
||||
#define SLEEP_TIME_MAX 60
|
||||
|
||||
// rc_mond shared memory 접근을 위한 key 파일.
|
||||
#define RC_MOND_SHARED_MEMORY_KEY_FILE "/user/service/rc_mond.shm"
|
||||
|
||||
// Rebalance 처리 대상 FHS 관련 FHS 의 최소 Storage 사용률 제한값.
|
||||
// 해당 percent(%) 이상인 장비만 선정되도록 하기 위함
|
||||
#define REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT 50
|
||||
|
||||
// Rebalance 처리 대상 FHS 관련 FHS 의 최대 disk busy 제한값.
|
||||
// 해당 percent(%) 이하인 장비만 선정되도록 하기 위함
|
||||
#define REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT 50.0
|
||||
|
||||
// Rebalance 대상 FHS 선정을 위한 score 계산시 disk_max_busy 항목에 부여할 가중치 정보.
|
||||
// - 점수가 높은 FHS 장비가 선정되므로...
|
||||
// - disk busy 상태인 장비는 선택되지 않도록 0.5 수준으로 설정
|
||||
// - storage_max_usage : disk_max_busy = 2 : 1 로 계산
|
||||
#define REBALANCE_TARGET_DISK_MAX_BUSY_WEIGHT 0.5
|
||||
|
||||
// Rebalance 대상 FHS 정보를 저장한 list 의 유효시간 (sec)
|
||||
// - SLEEP_TIME_MAX 값보다 커야 함.
|
||||
#define TIMEOUT_REBALANCE_LIST 120
|
||||
|
||||
// Copy 수신 가능 FHS 의 Disk 여유용량 최소값
|
||||
// - 1 TB
|
||||
#define COPY_TARGET_DISK_MIN_AVAILABLE_LIMIT (1099511627776ULL)
|
||||
|
||||
// COPY 수신 가능 FHS 선정을 위한 score 계산시 disk_max_busy 항목에 부여할 가중치 정보.
|
||||
// - 참고로 rc_mond 의 Top FHS 선정은 storage_max_usage : disk_max_busy = 1 : 4 로 계산하여 처리
|
||||
// - Copy Target 은 실제 copy 처리를 수행하는 thread 가 많지 않고...
|
||||
// - 되도록 여유 용량이 많은 장비에 copy 하는 것이 효과적이므로...
|
||||
// - storage_max_usage : disk_max_busy = 1 : 2 로 계산
|
||||
#define COPY_TARGET_DISK_MAX_BUSY_WEIGHT 2.0
|
||||
|
||||
// Copy 대상 FHS 정보를 저장한 list 의 유효시간 (sec)
|
||||
// - default sleep time 값보다 커야 함
|
||||
#define TIMEOUT_COPY_LIST 30
|
||||
|
||||
|
||||
// 공유메모리에 저장된 FHS 정보 중 적합한 FHS 정보를 찾기 위해
|
||||
// FHS 와 각 점수 정보를 저장하기 위한 구조체
|
||||
struct FhsScore {
|
||||
|
||||
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용됨.
|
||||
double score; // 계산된 score, 작을수록 idle
|
||||
};
|
||||
|
||||
|
||||
|
||||
CShmInfoThread::CShmInfoThread()
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
m_threadHandle = 0;
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
m_listRebalanceFhs.clear();
|
||||
m_listCopyTargetFhs.clear();
|
||||
|
||||
// 시간 정보 초기화.
|
||||
memset( &m_timeLastRefreshRebalanceFhs, 0x00, sizeof( struct timespec ) );
|
||||
memset( &m_timeLastRefreshCopyTargetFhs, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
pthread_mutex_init( &m_mutexRebalanceFhs, NULL );
|
||||
pthread_mutex_init( &m_mutexCopyTargetFhs, NULL );
|
||||
|
||||
}
|
||||
|
||||
CShmInfoThread::~CShmInfoThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
pthread_mutex_destroy( &m_mutexRebalanceFhs );
|
||||
pthread_mutex_destroy( &m_mutexCopyTargetFhs );
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
bool CShmInfoThread::Start()
|
||||
{
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CShmInfoThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[SHM INF] thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::GetRebalanceTargetFhs( std::string & szResultHost, std::list<std::string>& listIgnoreFHS )
|
||||
{
|
||||
// Rebalance 대상 FHS 목록 중....
|
||||
// 전달받은 listIgnoreFHS 에 등록된 FHS 정보를 제외하고....
|
||||
// 남은 FHS 정보 중.. 맨 처음 나오는 정보를 전달한다.
|
||||
|
||||
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래된 경우.... 버린다.
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshRebalanceFhs.tv_sec > TIMEOUT_REBALANCE_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] rebalance list expired. %d sec.", TIMEOUT_REBALANCE_LIST );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 저장된 list 정보가 존재하지 않는 경우... false 반환 처리
|
||||
if( m_listRebalanceFhs.size() <= 0 )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] rebalance list empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator it;
|
||||
std::list<std::string>::const_iterator itIgnore;
|
||||
std::string szHost;
|
||||
|
||||
pthread_mutex_lock( &m_mutexRebalanceFhs );
|
||||
|
||||
for( it = m_listRebalanceFhs.begin(); it != m_listRebalanceFhs.end(); ++it )
|
||||
{
|
||||
szHost = *it;
|
||||
|
||||
// 추출된 FHS 가 ignore list 에 존재하는지 확인
|
||||
itIgnore = std::find( listIgnoreFHS.begin(), listIgnoreFHS.end(), szHost );
|
||||
if( itIgnore == listIgnoreFHS.end() )
|
||||
{
|
||||
// ignore list 에 존재하지 않는 경우...
|
||||
// 해당 장비를 선택하고.. loop 종료 처리
|
||||
szResultHost = szHost;
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper rebalance fhs [%s] selected.",szResultHost.c_str() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 적합한 장비를 찾지 못한 경우.
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper rebalance fhs not selectd. total[%lu], ignore[%lu]."
|
||||
, m_listRebalanceFhs.size(), listIgnoreFHS.size() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy content 를 저장할 target FHS 정보를 반환
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - 복사 가능 FHS 가 존재하는지 검색 관련 오류 발생시 반환값 정보를 좀더 세분화 처리
|
||||
// - 정상 처리시 : return 1
|
||||
// - 복사 가능 물리적 FHS 가 없는 경우 : return -1
|
||||
// - 복사 가능 물리 FHS 는 존재하지만 ignore 목록에 포함되어.. 실제 복사 가능 FHS 가 없는 경우 : return 0
|
||||
int CShmInfoThread::GetCopyTargetFhs( std::string & szResultHost, std::list<std::string>& listIgnoreFHS )
|
||||
{
|
||||
// COPY 수신 가능 FHS list 에서
|
||||
// 전달받은 Ignore FHS 에 등록된 FHS 를 제외하고
|
||||
// 남은 FHS 중 맨 처음 나오는 정보를 전달한다.
|
||||
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래된 경우.... 버린다.
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshCopyTargetFhs.tv_sec > TIMEOUT_COPY_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target info list expired. %d sec.", TIMEOUT_COPY_LIST );
|
||||
|
||||
// 복사 가능 물리 FHS 가 아예 없는 경우... -1 return
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 2. 저장된 list 정보가 존재하지 않는 경우
|
||||
if( m_listCopyTargetFhs.size() <= 0 )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] ] copy target info list empty." );
|
||||
|
||||
// 복사 가능 물리 FHS 가 아예 없는 경우... -1 return
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
std::list<std::string>::const_iterator it;
|
||||
std::list<std::string>::const_iterator itIgnore;
|
||||
std::string szHost;
|
||||
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
|
||||
for( it = m_listCopyTargetFhs.begin(); it != m_listCopyTargetFhs.end(); ++it )
|
||||
{
|
||||
szHost = *it;
|
||||
|
||||
// 추출된 FHS 가 ignore list 에 존재하는지 확인
|
||||
itIgnore = std::find( listIgnoreFHS.begin(), listIgnoreFHS.end(), szHost );
|
||||
if( itIgnore == listIgnoreFHS.end() )
|
||||
{
|
||||
// ignore list 에 존재하지 않는 경우...
|
||||
// 해당 장비를 선택하고.. loop 종료 처리
|
||||
szResultHost = szHost;
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper copy target [%s] selected.", szResultHost.c_str() );
|
||||
|
||||
// 복사 가능 물리 FHS 가 존재하는 경우 ... 1 return
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 적합한 장비를 찾지 못한 경우.
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
LOG( LWAR, "[SHM INF] proper copy target fhs not selectd. total[%lu], ignore[%lu]."
|
||||
, m_listCopyTargetFhs.size(), listIgnoreFHS.size() );
|
||||
|
||||
// 복사 가능 물리 FHS 가 존재하지만.. ignore 에 포함되어.. 실제 가능 FHS 가 전혀 없는 경우 ... 0 return
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::IsValidCopyTargetFhs( void )
|
||||
{
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래되었는지 check
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
bool bResult = true;
|
||||
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshCopyTargetFhs.tv_sec > TIMEOUT_COPY_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target list expired. %d sec.", TIMEOUT_COPY_LIST );
|
||||
bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_listCopyTargetFhs.empty() == true )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target list empty." );
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
void * CShmInfoThread::threadFunc( void * arg )
|
||||
{
|
||||
CShmInfoThread * pObject = reinterpret_cast<CShmInfoThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CShmInfoThread::Execute()
|
||||
{
|
||||
// 다음 sleep time 은 default 로 처리
|
||||
// - 하위 flow 에서 문제 발생하면 sleep time 을 max 로 조정
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// rc_mond 가 기동 중인지 check
|
||||
// - 미기동 상태이면... 기동시까지 대기.
|
||||
if( IsRunRcMonD() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s process not running. max sleep %u sec.", PROCESS_NAME_RC_MONITORING_DAEMON, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 공유메모리 접근
|
||||
key_t iShmKey;
|
||||
|
||||
// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
|
||||
FILE * pShmFile = fopen( RC_MOND_SHARED_MEMORY_KEY_FILE, "r" );
|
||||
|
||||
if( pShmFile == NULL )
|
||||
{
|
||||
// 공유메모리 접근 key file 존재하지 않는 경우....
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory key file(%s) not exist. max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON , RC_MOND_SHARED_MEMORY_KEY_FILE , m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 공유메모리 접근 key file 존재하는 경우
|
||||
fclose( pShmFile );
|
||||
pShmFile = NULL;
|
||||
}
|
||||
|
||||
// 공유메모리 접근 key 값 추출
|
||||
iShmKey = ftok( RC_MOND_SHARED_MEMORY_KEY_FILE, 1 );
|
||||
if( iShmKey == (key_t)-1 )
|
||||
{
|
||||
// 공유메모리 접근 key 획득 실패시
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory key generate fail with file(%s). max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON , RC_MOND_SHARED_MEMORY_KEY_FILE , m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 공유메모리 attach 처리
|
||||
CShmControl< struct RcInfo > shmCtl;
|
||||
if( shmCtl.Attach( iShmKey, sizeof( struct RcInfo ) ) == false )
|
||||
{
|
||||
// 공유메모리 접근 실패시
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory attach fail with key(%d). max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON, iShmKey, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LDBG, "[SHM INF] start." );
|
||||
|
||||
// 공유메모리 최상위 접근 포인터 획득
|
||||
struct RcInfo * pShm = shmCtl.GetShmPtr();
|
||||
|
||||
// copy 처리 수신 가능 FHS 추출 및 rebalance 대상 FHS 추출 목록 update
|
||||
if( RefreshFhsList( pShm ) == false )
|
||||
{
|
||||
// 실패 발생시 다음 sleep time 최대 설정.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
}
|
||||
|
||||
// 공유메모리 접근 해제
|
||||
shmCtl.Detach();
|
||||
pShm = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool FhsScoreSortASC( const struct FhsScore & fhs1, const struct FhsScore & fhs2 )
|
||||
{
|
||||
return (fhs1.score < fhs2.score);
|
||||
}
|
||||
|
||||
bool FhsScoreSortDESC( const struct FhsScore & fhs1, const struct FhsScore & fhs2 )
|
||||
{
|
||||
return (fhs1.score > fhs2.score);
|
||||
}
|
||||
|
||||
|
||||
bool CShmInfoThread::RefreshFhsList( const struct RcInfo * const pShm )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
|
||||
// 1. Write 가능 FHS 의 Storage 사용량 정보를 조회하여 평균을 구한다.
|
||||
// - 전체 FHS 의 평균을 구하면.. 용량이 충분한데 장비 문제로 강제 업로드 막은 장비까지 계산되어
|
||||
// - 평균을 낮추 버리는 문제가 발생하여...
|
||||
// - reblance 수신 FHS 가 다시 rebalance 처리 대상 FHS 가 될 수 있는 문제가 생긴다.
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
unsigned int uFhsCount = 0;
|
||||
unsigned int uFhsTotalUsage = 0;
|
||||
int index = 0;
|
||||
unsigned int uFhsAverageUsage = 0;
|
||||
|
||||
for( index = 0; index <= pShm->client_max_index; index++ )
|
||||
{
|
||||
pClient = &(pShm->client[index]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// write 가능 정상 상태 이고
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& pClient->service.service_stat == SERVICE_STAT_OK
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
)
|
||||
{
|
||||
// 평균값 계산을 위한 값 처리
|
||||
uFhsTotalUsage += pClient->system.storage_max_usage;
|
||||
uFhsCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// write 가능 유효한 FHS 가 없는 경우
|
||||
if( uFhsCount < 1 )
|
||||
{
|
||||
_LOG( LWAR, "[SHM INF] starge average usage calc fail: write available fhs[%u] not exist.", uFhsCount );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Storage 사용량 평균값 계산
|
||||
uFhsAverageUsage = (uFhsTotalUsage / uFhsCount) + 1; // 소수점 짤려서.. 86.7% 여도 86 % 로 계산되므로.. 1 보정
|
||||
_LOG( LINF, "[SHM INF] starge average usage calc ok. write fhs count[%u], avg[%u %%]", uFhsCount, uFhsAverageUsage );
|
||||
|
||||
// 2. 평균 Storage 사용률 미만인 FHS 를 Copy 수신 FHS 로 선정한다.
|
||||
// - 만약 적합한 FHS 가 없는 경우 아래의 rebalance 대상 FHS 로 선정하지 않는다.
|
||||
if( RefreshCopyTargetFhsList( pShm, uFhsAverageUsage ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. rebalance 대상 FHS 로 선정한다.
|
||||
// - 평균 Storage 사용률이 70% 미만인 경우... 평균 Storage 사용률보다 3% 이상 높은 FHS 를 rebalance 대상 FHS 로 선정
|
||||
// - 평균 Storage 사용률이 70% 이상인 경우.. 평균 Storage 사용률보다 1% 이상 높은 FHS 를 rebalance 대상 FHS 로 설정
|
||||
unsigned int uRebalanceTargetStorageUsage = 0;
|
||||
if( uFhsAverageUsage < 70 )
|
||||
{
|
||||
uRebalanceTargetStorageUsage = uFhsAverageUsage + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
uRebalanceTargetStorageUsage = uFhsAverageUsage + 1;
|
||||
}
|
||||
|
||||
if( RefreshRebalanceFhsList( pShm, uRebalanceTargetStorageUsage ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::RefreshCopyTargetFhsList( const RcInfo * const pShm, unsigned int uStorageMaxUsage )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
struct FhsScore stFhsScore;
|
||||
std::vector<struct FhsScore> vecFhsRank;
|
||||
int nFhsCount = 0;
|
||||
|
||||
vecFhsRank.clear();
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 변수 사용하도록 수정 처리
|
||||
double dTargetFhsMaxDiskBusyLimit = CProcessConfig::GetInstance()->GetTargetFhsMaxDiskBusyLimit();
|
||||
|
||||
// write 가능 장비를 찾아.. score 계산.
|
||||
for( int i = 0; i <= pShm->client_max_index; i++ )
|
||||
{
|
||||
pClient = &(pShm->client[i]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// write 가능 상태
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
// disk 최대 사용율이 평균값 미만
|
||||
// disk busy 하지 않고.. 남은 유효 용량 역시 충분한 경우
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& pClient->service.service_stat == SERVICE_STAT_OK
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
&& pClient->system.storage_max_usage < (int)uStorageMaxUsage
|
||||
&& pClient->system.disk_max_busy <= dTargetFhsMaxDiskBusyLimit
|
||||
&& pClient->system.storage_avail >= COPY_TARGET_DISK_MIN_AVAILABLE_LIMIT
|
||||
)
|
||||
{
|
||||
// 임시 객체 초기화.
|
||||
memset( &stFhsScore, 0x00, sizeof( struct FhsScore ) );
|
||||
|
||||
// hostname 저장.
|
||||
strncpy( stFhsScore.hostname, pClient->hostname, HOST_NAME_LENGTH );
|
||||
|
||||
// write score 계산 처리.
|
||||
// - storage_max_usage 항목과
|
||||
// - disk_max_busy * 전달받은 가중치 값의 합.
|
||||
stFhsScore.score = (double)(pClient->system.storage_max_usage)
|
||||
+ ((pClient->system.disk_max_busy) * COPY_TARGET_DISK_MAX_BUSY_WEIGHT);
|
||||
|
||||
// vector 에 결과 저장
|
||||
vecFhsRank.push_back( stFhsScore );
|
||||
|
||||
// count
|
||||
nFhsCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// copy write 가능 장비가 존재하면
|
||||
if( nFhsCount > 0 )
|
||||
{
|
||||
if( nFhsCount > 1 )
|
||||
{
|
||||
// 가장 점수가 낮아 이상적인 장비 순서로 정렬
|
||||
std::sort( vecFhsRank.begin(), vecFhsRank.end(), FhsScoreSortASC );
|
||||
}
|
||||
|
||||
// lock 을 걸고...
|
||||
// m_listCopyTargetFhs 정보 및 m_timeLastRefreshCopyTargetFhs 정보 업데이트.
|
||||
std::vector<struct FhsScore>::iterator it;
|
||||
std::string szRefresList;
|
||||
|
||||
// 기존 list 에 저장된 정보 clear
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
m_listCopyTargetFhs.clear();
|
||||
|
||||
for( it = vecFhsRank.begin(); it != vecFhsRank.end(); ++it )
|
||||
{
|
||||
// 정렬된 순서대로 list 에 push 처리
|
||||
m_listCopyTargetFhs.push_back( it->hostname );
|
||||
|
||||
szRefresList.append( it->hostname );
|
||||
szRefresList.append( " " );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] copy target fhs list push_back.[%s].", it->hostname );
|
||||
}
|
||||
|
||||
// refresh time 정보 업데이트
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeLastRefreshCopyTargetFhs );
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
_LOG( LINF, "[SHM INF] copy target fhs list refreshed. usage avg limit[%u %%]. [%d][%s]"
|
||||
, uStorageMaxUsage, nFhsCount, szRefresList.c_str() );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy 수신 가능 FHS 가 없는 경우...
|
||||
// - 관련 내역 로깅
|
||||
_LOG( LWAR, "[SHM INF] copy target fhs not exist. usage avg limit[%u %%], busy limit[%.1f %%]"
|
||||
, uStorageMaxUsage, dTargetFhsMaxDiskBusyLimit );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CShmInfoThread::RefreshRebalanceFhsList( const RcInfo * const pShm, unsigned int uStorageMinUsage )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
struct FhsScore stFhsScore;
|
||||
std::vector<struct FhsScore> vecFhsRank;
|
||||
int nFhsCount = 0;
|
||||
|
||||
vecFhsRank.clear();
|
||||
|
||||
// rebalance 대상 장비를 찾아.. score 계산.
|
||||
for( int i = 0; i <= pShm->client_max_index; i++ )
|
||||
{
|
||||
pClient = &(pShm->client[i]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// read only 상태인 경우..
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
// disk 최대 사용율이 전달받은 값 이상이고
|
||||
// disk 최대 사용량이 REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT(%) 이상인 장비
|
||||
// disk busy 가 REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT (%) 이하인 장비.
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& ( pClient->service.service_stat == SERVICE_STAT_RONLY || pClient->service.service_stat == SERVICE_STAT_OK )
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
&& pClient->system.storage_max_usage >= (int)uStorageMinUsage
|
||||
&& pClient->system.storage_max_usage >= REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT
|
||||
&& pClient->system.disk_max_busy <= REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT )
|
||||
{
|
||||
|
||||
// 임시 객체 초기화.
|
||||
memset( &stFhsScore, 0x00, sizeof( struct FhsScore ) );
|
||||
|
||||
// hostname 저장.
|
||||
strncpy( stFhsScore.hostname, pClient->hostname, HOST_NAME_LENGTH );
|
||||
|
||||
// score 계산 처리.
|
||||
// - storage_max_usage 항목에서 disk_max_busy 값을 빼서 점수 계산
|
||||
stFhsScore.score = (double)(pClient->system.storage_max_usage)
|
||||
- ((pClient->system.disk_max_busy) * REBALANCE_TARGET_DISK_MAX_BUSY_WEIGHT);
|
||||
|
||||
// vector 에 결과 저장
|
||||
vecFhsRank.push_back( stFhsScore );
|
||||
|
||||
// result count 증가.
|
||||
nFhsCount += 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Rebalance 대상 장비가 존재하면
|
||||
if( nFhsCount > 0 )
|
||||
{
|
||||
if( nFhsCount > 1 )
|
||||
{
|
||||
// 가장 점수가 높은 순으로 정렬
|
||||
std::sort( vecFhsRank.begin(), vecFhsRank.end(), FhsScoreSortDESC );
|
||||
}
|
||||
|
||||
std::string szRefresList;
|
||||
|
||||
// lock 을 걸고...
|
||||
// m_listCopyTargetFhs 정보 및 m_timeLastRefreshCopyTargetFhs 정보 업데이트.
|
||||
std::vector<struct FhsScore>::iterator it;
|
||||
|
||||
// 기존 list 에 저장된 정보 clear
|
||||
pthread_mutex_lock( &m_mutexRebalanceFhs );
|
||||
m_listRebalanceFhs.clear();
|
||||
|
||||
for( it = vecFhsRank.begin(); it != vecFhsRank.end(); ++it )
|
||||
{
|
||||
// 정렬된 순서대로 list 에 push 처리
|
||||
m_listRebalanceFhs.push_back( it->hostname );
|
||||
|
||||
szRefresList.append( it->hostname );
|
||||
szRefresList.append( " " );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] rebalance fhs list push_back.[%s]", it->hostname );
|
||||
}
|
||||
|
||||
// refresh time 정보 업데이트
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeLastRefreshRebalanceFhs );
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LINF, "[SHM INF] rebalance fhs list refreshed. usage limit[%u %%]. [%d][%s]"
|
||||
, (uStorageMinUsage > REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT ? uStorageMinUsage : REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT)
|
||||
, nFhsCount, szRefresList.c_str() );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebalance 처리 대상 FHS 가 없는 경우...
|
||||
// - 관련 내역 로깅
|
||||
_LOG( LINF, "[SHM INF] rebalance fhs not exist with usage limit [%u %%], busy limit [%.2f %%]"
|
||||
, (uStorageMinUsage > REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT ? uStorageMinUsage : REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT)
|
||||
, REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
rc_balanced shared memory info header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/26
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __SHM_INFO_THREAD_H__
|
||||
#define __SHM_INFO_THREAD_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
|
||||
// CShmInfoThread class
|
||||
// - rc_mond 공유 메모리에 접근하여....
|
||||
// - 주기적으로 rebalance 처리할 FHS 정보를 조회하여 저장
|
||||
// - 주기적으로 Copy 수신 가능한 FHS 정보를 조회하여 저장
|
||||
// - 작업 처리 관련 각 모듈에서 필요한 정보 제공을 위한 인터페이스 제공 목적
|
||||
class CShmInfoThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CShmInfoThread();
|
||||
|
||||
// 소멸자
|
||||
~CShmInfoThread();
|
||||
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리
|
||||
bool Start( void );
|
||||
|
||||
// Rebalance 대상 FHS 정보를 반환
|
||||
bool GetRebalanceTargetFhs( std::string & szResultHost, std::list<std::string> & listIgnoreFHS );
|
||||
|
||||
// copy content 를 저장할 target FHS 정보를 반환
|
||||
int GetCopyTargetFhs( std::string & szResultHost, std::list<std::string> & listIgnoreFHS );
|
||||
|
||||
// Copy receive target FHS 정보가 유효한지 check 하기 위한 함수.
|
||||
bool IsValidCopyTargetFhs( void );
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread loop 마다 sleep time 처리 관련 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
std::list<std::string> m_listRebalanceFhs; // Rebalance 대상 FHS 정보를 저장할 list
|
||||
struct timespec m_timeLastRefreshRebalanceFhs; // m_listRebalanceFhs 의 마지막 refresh 시간 정보를 저장할 변수
|
||||
pthread_mutex_t m_mutexRebalanceFhs; // m_listRebalanceFhs 관련 lock 객체
|
||||
|
||||
|
||||
std::list<std::string> m_listCopyTargetFhs; // Copy 수신 처리 가능 FHS 정보를 저장할 list
|
||||
struct timespec m_timeLastRefreshCopyTargetFhs; // m_listCopyTargetFhs 의 마지막 refresh 시간 정보를 저장할 변수
|
||||
pthread_mutex_t m_mutexCopyTargetFhs; // m_listCopyTargetFhs 관련 lock 객체
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void *arg );
|
||||
|
||||
// 실제 정보 갱신을 처리하는 함수
|
||||
void Execute( void );
|
||||
|
||||
|
||||
// rc_mond 공유메모리에서 rebalance 대상 및 Copy 수신 대상 FHS 목록을 갱신 처리하는 함수
|
||||
bool RefreshFhsList( const struct RcInfo * const pShm );
|
||||
|
||||
// rc_mond 공유메모리에서 Copy 처리 수신 가능 FHS 목록을 갱신 처리하는 함수
|
||||
// - 전달받은 Storage Max usge 값 미만인 FHS 만 선정.
|
||||
bool RefreshCopyTargetFhsList( const struct RcInfo * const pShm, unsigned int uStorageMaxUsage );
|
||||
|
||||
// rc_mond 공유메모리에서 rebalance 대상 FHS 목록을 갱신 처리하는 함수
|
||||
// - 전달받은 Storage Min usge 값 이상인 FHS 만 선정.
|
||||
bool RefreshRebalanceFhsList( const struct RcInfo * const pShm, unsigned int uStorageMinUsage );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SHM_INFO_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,630 @@
|
||||
#include "TargetSelectThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Common.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 30
|
||||
#define SLEEP_TIME_MAX 60
|
||||
|
||||
|
||||
// Content 조회시 최대 조회 limit
|
||||
#define CONTENT_SELECT_MAX_LIMIT_COUNT 100
|
||||
|
||||
|
||||
// Content select query 수행 시간이 지정된 시간 이상일 경우 로깅 처리 (sec)
|
||||
#define LOGGING_MAX_TIME_QUERY_EXEC 60
|
||||
|
||||
|
||||
// m_pQueueMoveStandby queue 에 추가 push 를 방지를 제한값.
|
||||
// - Max Copy Thread 값에 2배수까지 허용한다.
|
||||
#define MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT (MAX_COPY_THREAD_COUNT * 2)
|
||||
|
||||
|
||||
|
||||
CTargetSelectThread::CTargetSelectThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CShmInfoThread * const pShmInfoThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby), m_pShmInfoThread( pShmInfoThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
m_listCompleteFhs.clear();
|
||||
|
||||
m_uTimeoutFhsReselection = 0;
|
||||
m_uMaxTransferlimit = 0;
|
||||
}
|
||||
|
||||
CTargetSelectThread::~CTargetSelectThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
m_listCompleteFhs.clear();
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pShmInfoThread != NULL )
|
||||
m_pShmInfoThread = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CTargetSelectThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "TargetSelectThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 전송 처리 관련 내부 변수 계산.
|
||||
CalcInternalLimit();
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CTargetSelectThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "TargetSelectThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CTargetSelectThread::threadFunc( void * arg )
|
||||
{
|
||||
CTargetSelectThread * pObject = reinterpret_cast<CTargetSelectThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CTargetSelectThread::Execute()
|
||||
{
|
||||
// sleep time 조정 처리
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// rc_mond 가 기동 중인지 check
|
||||
// - 미기동 상태이면... 기동시까지 대기.
|
||||
if( IsRunRcMonD() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SELECT ] %s process not running. max sleep %u sec.", PROCESS_NAME_RC_MONITORING_DAEMON, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// MOVE 처리 가능한 FHS 존재 여부 check
|
||||
// - 존재하지 않는 경우.. content select 해서 push 해 놓기만 하다보면....
|
||||
// - 결국 중복 content push 되는 상황 발생할 수 있으므로....
|
||||
// - MOVE 수신 가능한 FHS 가 존재하지 않은 경우... content select 되지 않도록 한다.
|
||||
if( m_pShmInfoThread->IsValidCopyTargetFhs() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] valid copy target fhs not exist. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// Move 처리 대상 content 정보를 저장하는 queue 가 특정값 이상이면
|
||||
// sleep time 을 조정하고.. 이번 에는 작업을 수행하지 않는다.
|
||||
// copy 처리 thread 수가 작아서... 병목 구간
|
||||
// 이 때 많은 양을 push 처리하며... 중복 발생 가능성이 높아짐
|
||||
// 따라서 해당 queue 가 지정된 값 이하인 경우에만 rebalance 대상을 선택 처리한다.
|
||||
|
||||
if( m_pQueueMoveStandby->GetSize() > MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] move queue count high [%u / %d]. job skip. max sleep %u sec."
|
||||
, m_pQueueMoveStandby->GetSize(), MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 선택되어 처리된 FHS 목록 중 expire 된 항목 제거하여 갱신 처리
|
||||
UpdateCompleteFhs();
|
||||
|
||||
// 재선택 방지를 위한 ignore fhs list 생성
|
||||
std::list<std::string> listIgnoreFHS;
|
||||
std::list<CCompleteFhsInfo>::iterator it;
|
||||
|
||||
listIgnoreFHS.clear();
|
||||
|
||||
for( it = m_listCompleteFhs.begin(); it != m_listCompleteFhs.end(); ++it )
|
||||
{
|
||||
listIgnoreFHS.push_back( it->m_szHostName );
|
||||
}
|
||||
|
||||
|
||||
// m_pShmInfoThread 객체를 이용하여 rebalance 처리할 FHS 목록을 가져온다.
|
||||
std::string szRebalanceHost;
|
||||
if( m_pShmInfoThread->GetRebalanceTargetFhs( szRebalanceHost, listIgnoreFHS ) == false )
|
||||
{
|
||||
// rebalance 대상 host 를 선정하지 못한 경우...
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] reblance fhs not selected. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
|
||||
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
|
||||
{
|
||||
// 연결 실패시...
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
LOG( LERR, "TargetSelectThread: RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 최종 선정된 FHS 에 대해 MOVE 처리할 최종 서비스를 선택한다.
|
||||
std::string szService;
|
||||
if( SelectService( rcdb, szService ) == false )
|
||||
{
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
// 서비스 선택 실패시...
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
LOG( LERR, "TargetSelectThread: Target service select failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
_LOG( LINF, "[SELECT ] start with host[%s], svc[%s]", szRebalanceHost.c_str(), szService.c_str() );
|
||||
|
||||
// 최종 선택된 FHS 를 작업 처리 list 에 등록처리한다
|
||||
InsertCompleteFhs( szRebalanceHost );
|
||||
|
||||
// 최종 선정된 FHS, 서비스에 대해 MOVE 처리할 content 정보를 조회하여...
|
||||
// Move 처리 대상 content 정보를 저장하는 queue 에 push_back 시킨다.
|
||||
SelectContent( rcdb, szRebalanceHost, szService );
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CTargetSelectThread::CalcInternalLimit( void )
|
||||
{
|
||||
// FHS 의 재선택 방지 관련 time limit 값을 sec 단위로 계산한다.
|
||||
// 300 ~ 86400
|
||||
m_uTimeoutFhsReselection = (CProcessConfig::GetInstance()->GetFhsReselectionTimeLimit()) * 60;
|
||||
|
||||
// rebalance 대상 장비에서 1회 Move 처리시킬 Content 의 최대 크기 계산
|
||||
// 1. 각 copy thread 당 100Mbps 전송으로 계산 : 100Mbps -> 12 Mbyte / sec -> 12000000ULL
|
||||
// 2. MAX_COPY_THREAD_COUNT 정의된 Copy thread count
|
||||
// 3. 전송 시간
|
||||
// - 일반 상황 : FHS 재선택 방지시간
|
||||
// - full hash : FHS 재선택 방지시간 / 2
|
||||
// 4. 안전계수
|
||||
// - 0.7
|
||||
|
||||
// full hash check 선택된 경우
|
||||
if( CProcessConfig::GetInstance()->GetHashCheckLevel() == 2 )
|
||||
{
|
||||
// Copy Thread 가 1개인 경우, N 개인 경우 본 값에 곱하면 됨.
|
||||
// - 재선택 방지 시간이 30 min = 7559999999 = 7.5 GB
|
||||
// - 재선택 방지 시간이 20 min = 5040000000 = 5.0 GB
|
||||
// - 재선택 방지 시간이 60 min = 15119999999 = 15.1 GB
|
||||
// - 재선택 방지 시간이 1 day = 362880000000 = 362 GB
|
||||
|
||||
m_uMaxTransferlimit = (unsigned long long)(12000000ULL * MAX_COPY_THREAD_COUNT * m_uTimeoutFhsReselection / 2 * 7 / 10);
|
||||
}
|
||||
else // full hash 가 아닌.. No check 또는 앞뒤 1MByte check 인 경우.
|
||||
{
|
||||
// Copy Thread 가 1개인 경우, N 개인 경우 본 값에 곱하면 됨.
|
||||
// - 재선택 방지 시간이 30 min = 15119999999 = 15 GB
|
||||
// - 재선택 방지 시간이 20 min = 10080000000 = 10 GB
|
||||
// - 재선택 방지 시간이 60 min = 30239999999 = 30 GB
|
||||
// - 재선택 방지 시간이 1 day = 725760000000 = 725 GB
|
||||
|
||||
m_uMaxTransferlimit = (unsigned long long)(12000000ULL * MAX_COPY_THREAD_COUNT * m_uTimeoutFhsReselection * 7 / 10);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CTargetSelectThread::UpdateCompleteFhs( )
|
||||
{
|
||||
time_t timeNow;
|
||||
time( &timeNow );
|
||||
|
||||
std::list<CCompleteFhsInfo>::iterator it;
|
||||
|
||||
// list 의 erase() 함수 사용시 문제를 해결하기 위해 아래와 같이 처리함.
|
||||
it = m_listCompleteFhs.begin();
|
||||
while( it != m_listCompleteFhs.end() )
|
||||
{
|
||||
if( (timeNow - it->m_timeSelect) > m_uTimeoutFhsReselection )
|
||||
{
|
||||
// expire 시간을 넘긴 경우.. 해당 FHS 삭제 처리
|
||||
_LOG( LINF, "[SELECT ] fhs %s re-select limit expire.", it->m_szHostName.c_str() );
|
||||
|
||||
// list 삭제시 다음 원소 위치 접근 실패 오류를 해결하기 위해 반환값을 it 에 설정 처리.
|
||||
it = m_listCompleteFhs.erase( it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// expire 시간을 넘기지 않은 경우...
|
||||
// queue 개념의 list 라서 다음 FHS 도 expire 시간이 되지 않았으므로... 더이상 check 하지 않는다.
|
||||
// it++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CTargetSelectThread::InsertCompleteFhs( std::string & szHost )
|
||||
{
|
||||
CCompleteFhsInfo fhs;
|
||||
|
||||
fhs.m_szHostName = szHost;
|
||||
// 시간은 CCompleteFhsInfo 객체 생성시 자동으로 생성됨
|
||||
|
||||
// list push.
|
||||
m_listCompleteFhs.push_back( fhs );
|
||||
|
||||
_LOG( LINF, "[SELECT ] fhs re-select limit list push. [%s current:%ld expire:+%u sec]"
|
||||
, fhs.m_szHostName.c_str(), fhs.m_timeSelect, m_uTimeoutFhsReselection );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool CTargetSelectThread::SelectService( DataBase & rcdb, std::string & szService )
|
||||
{
|
||||
// MOVE 처리를 수행할 서비스를 RCDB 에서 선택한다.
|
||||
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 1. 서비스 실제 사용량이 1TB 이상인 서비스를 선택
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT sp_svc_tran_id FROM t_dav_use_dir WHERE depth=0 AND dir_used >= 1000000000000; "
|
||||
);
|
||||
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] service select exec error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] service select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 적합한 서비스가 없는 경우..
|
||||
LOG( LWAR, "[SELECT ] service select not found. [%d][%s]"
|
||||
, nResult, szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 1 )
|
||||
{
|
||||
// 조회 결과가 1개인 경우...
|
||||
szService = rcdb.GetValue( 0, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// N 개의 조회 결과가 존재하는 경우
|
||||
// random 처리하여 1개를 선택한다.
|
||||
|
||||
// random 선택
|
||||
unsigned int uRandom = 0;
|
||||
int fd = open( "/dev/urandom", O_RDONLY );
|
||||
read( fd, &uRandom, sizeof( uRandom ) );
|
||||
close( fd );
|
||||
|
||||
szService = rcdb.GetValue( uRandom % nResult, 0 );
|
||||
}
|
||||
|
||||
rcdb.PgClear();
|
||||
|
||||
// 적합한 서비스 정보 결과 로깅
|
||||
_LOG( LINF, "[SELECT ] service select ok. selected svc_seq [%s]. total svc count [%d]"
|
||||
, szService.c_str() , nResult );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTargetSelectThread::SelectContent( DataBase & rcdb, std::string & szRebalanceHost, std::string & szService )
|
||||
{
|
||||
// RCDB 에서 해당 host, service 에 해당하는 content 를 조회하여 멤버변수인 queue 에 push 처리한다.
|
||||
|
||||
|
||||
// Query 수행 시간 check 를 위한 변수
|
||||
struct timespec timeStart, timeNow;
|
||||
double timeElapsed = 0.0;
|
||||
memset( &timeStart, 0x00, sizeof( struct timespec ) );
|
||||
memset( &timeNow, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeStart );
|
||||
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 대상 content 조회
|
||||
// - 삭제되지 않았고...
|
||||
// - 원본, 복제본 만.. cache 는 포함하지 않는다.
|
||||
// - 0 파일 제외하고 최소 3 MB 이상 파일
|
||||
// - 최종 수정일이 15일 이상된 content
|
||||
// - 가장 용량이 큰 순서부터
|
||||
// 1. 최소한의 이동으로 FHS 간 균형을 빨리 맞추기 위함
|
||||
// 2. 작은 파일 많이 이동시키면.. RCDB blot 되므로... 용량 큰 파일이 유리
|
||||
// 3. 오래된 파일부터 이동시키면... 실패시에도 계속 오래된 파일부터 시도하므로...
|
||||
// 4. 용량은 m_uMaxTransferlimit 값 이상은 옮길 수 없으므로.. 해당 크기 이하로 설정한다.
|
||||
|
||||
// 기능 수정
|
||||
// - 제한크기 값에 해당하는 content 1개만 선택되었지만.. 해당 content 가 이미 copy 대상 FHS 에 존재하는 경우
|
||||
// - 해당 FHS 는 매번 조회해도 해당 content 만 나와.. 실패처리되어.. rebalance 안되는 상황 발생
|
||||
// - 이를 해결하고자.... 조회시 크기 제한 값을 1GB ~ 전송 제한값 수준까지 GB 단위로 random으로 계산하여 조회되도록 하여
|
||||
// - 문제 content 조회 되지 않을 가능성이 존재하도록 처리.
|
||||
|
||||
// 최대 전송 제한값에서 GB 단위 값을 추출
|
||||
// - 1GB 이하이면 1 로 처리.
|
||||
unsigned long long uSelectSizeLimit = m_uMaxTransferlimit / 1000000000ULL;
|
||||
if( uSelectSizeLimit == 0 )
|
||||
uSelectSizeLimit = 1;
|
||||
|
||||
// nano 시간값을 1~ 전송제한크기 GB 단위로 처리하여 GB 단위 random 값을 얻은 후
|
||||
// 실제 전송 제한값에서 해당 크기만큼 빼서
|
||||
// 조회시 사용할 GB 단위의 크기값을 생성.
|
||||
uSelectSizeLimit = m_uMaxTransferlimit - ((timeStart.tv_nsec % uSelectSizeLimit) * 1000000000ULL);
|
||||
_LOG( LINF, "[SELECT ] select content size limit. [%llu GB]", uSelectSizeLimit/1000/1000/1000 );
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - PC-71 에서 조회 후 선택된 content 가 이미 대상 장비에 복제본 등이 존재하여 이동이 실패할 경우....
|
||||
// - 다음 조회시에도 계속 같은 content 가 다시 선택되는 문제가 발생함.
|
||||
// - 따라서 조회시 order by 를 content length 가 아닌 random 으로 처리하는 기능 추가
|
||||
|
||||
// select mode 사용 전 기존 mode 를 timeout 처리해야 하는지 확인 처리
|
||||
CProcessConfig::GetInstance()->SetContentSelectModeToDefault();
|
||||
|
||||
if( CProcessConfig::GetInstance()->GetContentSelectMode() == SELECT_MODE_RANDOM )
|
||||
{
|
||||
_LOG( LINF, "[SELECT ] content select mode : RANDOM " );
|
||||
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT resource_id, uri, sp_svc_tran_id, host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
"AND host_name = '%s' "
|
||||
"AND get_content_length >= 3000000 AND get_content_length < %llu "
|
||||
"AND get_lastmodified < (SELECT date_part( 'epoch', CURRENT_TIMESTAMP )::int - 1296000 || '000000')::bigint "
|
||||
"ORDER BY random() LIMIT %d;"
|
||||
, szService.c_str()
|
||||
, szRebalanceHost.c_str()
|
||||
, uSelectSizeLimit
|
||||
, CONTENT_SELECT_MAX_LIMIT_COUNT
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default 상황인 경우.. 큰 파일을 우선으로 이동시킨다.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT resource_id, uri, sp_svc_tran_id, host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
"AND host_name = '%s' "
|
||||
"AND get_content_length >= 3000000 AND get_content_length < %llu "
|
||||
"AND get_lastmodified < (SELECT date_part( 'epoch', CURRENT_TIMESTAMP )::int - 1296000 || '000000')::bigint "
|
||||
"ORDER BY get_content_length DESC LIMIT %d;"
|
||||
, szService.c_str()
|
||||
, szRebalanceHost.c_str()
|
||||
, uSelectSizeLimit
|
||||
, CONTENT_SELECT_MAX_LIMIT_COUNT
|
||||
);
|
||||
}
|
||||
|
||||
// Query 실행
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
|
||||
// Query 수행시간이 지정된 시간 이상 걸리면 로깅....
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
timeElapsed = (double)((timeNow.tv_sec - timeStart.tv_sec) * 1.0e9 + (timeNow.tv_nsec - timeStart.tv_nsec)) / 1.0e9;
|
||||
|
||||
if( timeElapsed >= (double)LOGGING_MAX_TIME_QUERY_EXEC )
|
||||
{
|
||||
_LOG( LWAR, "[SELECT ] query execute time too long [%.2lf sec]. result[%d] [%s]"
|
||||
, timeElapsed, nResult, szQuery );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LDBG, "[SELECT ] query execute [%.2lf sec]. result[%d] [%s]"
|
||||
, timeElapsed, nResult, szQuery );
|
||||
}
|
||||
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] content select exec error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int uPushTotalCount = 0;
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
|
||||
_LOG( LDBG, "[SELECT ] content selected. [%s svc:%s select_count:%d ]"
|
||||
, szRebalanceHost.c_str(), szService.c_str(), nResult );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] content select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 적합한 Content 가 없는 경우..
|
||||
LOG( LWAR, "[SELECT ] content select not found. [%d][svc:%s][%s][%s]"
|
||||
, nResult, szService.c_str(), szRebalanceHost.c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// N 개의 조회 결과가 존재하는 경우
|
||||
// - 정해진 용량까지 Queue 에 push 처리한다.
|
||||
CRebalanceData data;
|
||||
unsigned long long uPushSum = 0;
|
||||
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
// 0: resouce_id
|
||||
// 1: uri
|
||||
// 2: svc seq
|
||||
// 3: host_name
|
||||
// 4: filename_hash
|
||||
// 5: get_content_legnth
|
||||
data.m_uOriginContentLength = atoll( rcdb.GetValue( i, 5 ) );
|
||||
|
||||
if( uPushSum + data.m_uOriginContentLength > m_uMaxTransferlimit )
|
||||
{
|
||||
// 더 이상 push 처리하지 못하도록 한다.
|
||||
_LOG( LINF, "[SELECT ] content push end by total size %llu GB limit. [%s svc:%s select:%d, push:%u]"
|
||||
, m_uMaxTransferlimit /1000/1000/1000
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, nResult, uPushTotalCount );
|
||||
break;
|
||||
}
|
||||
|
||||
uPushSum += data.m_uOriginContentLength;
|
||||
|
||||
// 확인 결과 resouce_id 의 최대값은 9223372036854775807
|
||||
// long long int 의 최대값은 9223372036854775807 으로 resouce_id 값과 동일
|
||||
// 실제 변환 테스트해 보니 최대값까지 정상 변환 확인함.
|
||||
data.m_uResourceId = atoll( rcdb.GetValue( i, 0 ) );
|
||||
data.m_szURI = rcdb.GetValue( i, 1 );
|
||||
data.m_szServiceSeq = rcdb.GetValue( i, 2 );
|
||||
data.m_szOriginHost = rcdb.GetValue( i, 3 );
|
||||
data.m_szOriginFileNameHash = rcdb.GetValue( i, 4 );
|
||||
|
||||
// push 처리
|
||||
if( m_pQueueMoveStandby->Push( data ) == false )
|
||||
{
|
||||
// push 처리 실패시...
|
||||
// 2 sec 후 재시도해서 실패하면.. queue full 가능성이 있으므로...
|
||||
// 버리고.. loop 중지
|
||||
|
||||
sleep( 2 );
|
||||
if( m_pQueueMoveStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LWAR, "[SELECT ] content select ok. but queue push fail. [%s] [svc:%s id:%llu size:%llu]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// total push count 1 증가
|
||||
uPushTotalCount++;
|
||||
|
||||
// 로깅
|
||||
_LOG( LINF, "[SELECT ] content select, push ok. [%s] [svc:%s id:%llu size:%llu]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
}
|
||||
}
|
||||
|
||||
rcdb.PgClear();
|
||||
|
||||
// Content 선택 결과 로깅
|
||||
_LOG( LINF, "[SELECT ] content push end. [%s svc:%s select:%d, push:%u]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, nResult, uPushTotalCount );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/****************************************************************************
|
||||
rc_balanced target content select thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/14
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __TARGET_SELECT_THREAD_H__
|
||||
#define __TARGET_SELECT_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
#include "Database.h"
|
||||
#include "ShmInfoThread.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
|
||||
// 한번 선정되어 rebalance 처리한 FHS 정보 저장을 위한 객체
|
||||
class CCompleteFhsInfo
|
||||
{
|
||||
public:
|
||||
|
||||
// 마지막 선택된 시간
|
||||
time_t m_timeSelect;
|
||||
|
||||
// 장비 name
|
||||
std::string m_szHostName;
|
||||
|
||||
// 생성자 (마지막 선택 시간은 현재 시간으로 설정 처리)
|
||||
inline CCompleteFhsInfo() { time( &m_timeSelect ); }
|
||||
};
|
||||
|
||||
|
||||
// CTargetSelectThread class
|
||||
// - rebalance 처리 대상 FHS 선정 및 Content 선정 처리를 수행
|
||||
// - move 처리할 content 정보를 move standby queue 에 저장 처리
|
||||
// - 한번 선정된 FHS 는 특정 시간동안 재선정 되지 않는다.
|
||||
class CTargetSelectThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CTargetSelectThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CShmInfoThread * const pShmInfoThread );
|
||||
|
||||
// 소멸자
|
||||
~CTargetSelectThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
// Move 처리 대상 content 정보를 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// Sharde memory 정보를 추출하여 관리하는 Thread 객체에 대한 포인터
|
||||
CShmInfoThread * m_pShmInfoThread;
|
||||
|
||||
// 선택되어 MOVE 처리한 FHS 는 특정시간동안 다시 선택되지 않도록 목록으로 관리
|
||||
std::list<CCompleteFhsInfo> m_listCompleteFhs;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
// 선택되어 MOVE 처리한 FHS 의 특정시간 동안 재선택을 방지하기 위한 Timeout 값
|
||||
// 단위: sec
|
||||
// - conf 설정 등을 참고로 초기화시 자동으로 계산됨.
|
||||
unsigned int m_uTimeoutFhsReselection;
|
||||
|
||||
// Rebalance 대상 장비로 선택된 FHS 에서 한번 선택시 전송 처리할 Content 의 최대 크기
|
||||
unsigned long long m_uMaxTransferlimit;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// FHS 의 재선택 방지 관련 timeout 및 최대 전송 가능 제한값을 계산한다.
|
||||
bool CalcInternalLimit( void );
|
||||
|
||||
// m_queueCompleteFhs 에서 expire 시간이 지난 FHS 정보 삭제 처리.
|
||||
void UpdateCompleteFhs( void );
|
||||
|
||||
// m_queueCompleteFhs 에 FHS 정보 등록
|
||||
void InsertCompleteFhs( std::string & szHost );
|
||||
|
||||
|
||||
// RCDB 에서 Move 처리할 대상 서비스를 선택한다.
|
||||
bool SelectService( DataBase &rcdb, std::string & szService );
|
||||
|
||||
// RCDB 에서 Move 처리할 Content 를 조회하여.. queue 에 push 처리한다.
|
||||
bool SelectContent( DataBase &rcdb, std::string & szRebalanceHost, std::string & szService );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __TARGET_SELECT_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#include "WorkerRebalance.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "ProcessMonitoringThread.h"
|
||||
#include "ShmInfoThread.h"
|
||||
#include "TargetSelectThread.h"
|
||||
#include "CopyControlThread.h"
|
||||
#include "RcdbUpdateThread.h"
|
||||
#include "PhysicalDeleteThread.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExitWorkerRebalance()
|
||||
{
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Worker Rebalance 프로세스에 대한 대한 종료 처리 수신시.
|
||||
static void SignalWorkerRebalanceTerminate( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LINF, "[Rebalance:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
// Worker::Rebalance Process 의 signal 처리기...
|
||||
void SetWorkerRebalanceSignalHandler()
|
||||
{
|
||||
sigset_t set;
|
||||
struct sigaction act;
|
||||
|
||||
memset( &act, 0x00, sizeof( act ) );
|
||||
sigfillset( &set );
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
sigfillset( &act.sa_mask );
|
||||
|
||||
// 무시 처리 signal
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
|
||||
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
|
||||
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
|
||||
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
|
||||
|
||||
// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction( SIGCHLD, &act, NULL );
|
||||
|
||||
/* 각종 에러나 사용자의 종료 신호 처리 */
|
||||
act.sa_handler = SignalWorkerRebalanceTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Worker::Rebalance 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerRebalanceMain()
|
||||
{
|
||||
// Process Rename..
|
||||
set_ps_display( PROG_NAME": Rebalance init.", false );
|
||||
|
||||
// Set Signal Handler
|
||||
SetWorkerRebalanceSignalHandler();
|
||||
|
||||
|
||||
// 1. 사용할 객체 초기화 처리
|
||||
|
||||
// Move 처리할 대상 content 를 저장할 Queue 객체
|
||||
CDataQueue<CRebalanceData> queueMoveStandby;
|
||||
queueMoveStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// Move 대상 conent 를 다른 FHS 에 Copy 완료한 후 RCDB 업데이트 수행할 content 정보를 저장하기 위한 queue.
|
||||
CDataQueue<CRebalanceData> queueUpdateStandby;
|
||||
queueUpdateStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// RCDB UPDATE 처리 후 delete(unlink) 처리 대상 content 를 저장하기 위한 queue
|
||||
CDataQueue<CRebalanceData> queueDeleteStandby;
|
||||
queueDeleteStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// Shared memory 접근 및 주요 정보 추출 관련 thread 객체 생성
|
||||
CShmInfoThread objShmInfoThread;
|
||||
|
||||
// Rebalance 처리 대상 FHS, content 선정을 수행하는 thread 객체 생성
|
||||
CTargetSelectThread objTargetSelectThread( &queueMoveStandby, &objShmInfoThread );
|
||||
|
||||
// Content Copy 처리를 수행할 thread 객체 생성
|
||||
CCopyControlThread objCopyControlThread( &queueMoveStandby, &queueUpdateStandby, &objShmInfoThread );
|
||||
|
||||
// RCDB update 처리를 수행할 thread 객체 생성
|
||||
CRcdbUpdateThread objRcdbUpdateThread( &queueUpdateStandby, &queueDeleteStandby );
|
||||
|
||||
// Process Monitoring thread 객체 생성
|
||||
CProcessMonitoringThread objProcessMonitoringThread( &queueMoveStandby, &queueUpdateStandby, &queueDeleteStandby, &objCopyControlThread );
|
||||
|
||||
// 물리 파일 삭제(unlink) 처리 thread 객체 생성
|
||||
CPhysicalDeleteThread objPhysicalDeleteThread( &queueDeleteStandby, &objProcessMonitoringThread );
|
||||
|
||||
|
||||
|
||||
// 2. 각 모듈 기동 처리
|
||||
// - 최종 작업 모듈부터 기동 처리
|
||||
|
||||
set_ps_display( PROG_NAME": Rebalance start.", false );
|
||||
|
||||
// 공유메모리 접근 및 Rebalance 처리 대상, Copy 수신 대상 FHS 정보 수집을 담당하는 Thread 기동 처리
|
||||
// - 정상 기동시 공유메모리 정보를 scan 해 올 수 있도록 잠시 대기한다.
|
||||
if( objShmInfoThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Shm info thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
else
|
||||
{
|
||||
sleep( 3 ); // 성공시 3 sec 대기
|
||||
}
|
||||
|
||||
|
||||
// 물리 파일 삭제(unlink) 처리를 수행할 thread 기동 처리.
|
||||
if( objPhysicalDeleteThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Physical delete thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// RCDB update 처리를 수행할 thread 기동 처리.
|
||||
if( objRcdbUpdateThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Rcdb update thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// Content copy 처리를 수행할 thread 기동 처리.
|
||||
if( objCopyControlThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Content copy control thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// rebalance 처리 대상 FHS, content 선정을 수행하는 thread 기동 처리
|
||||
if( objTargetSelectThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Target select thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 프로세스 모니터링 thread 기동 처리.
|
||||
if( objProcessMonitoringThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Process monitoring thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 대기
|
||||
while( 1 )
|
||||
{
|
||||
// 할일 없어서.. 시그널 수신까지 대기 한다.
|
||||
pause();
|
||||
}
|
||||
|
||||
// 종료시 정리 작업 수행
|
||||
// - 실제로는 동작하지 않는 code
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
Rebalance worker process main
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/25
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __WORKER_REBALANCE_H__
|
||||
#define __WORKER_REBALANCE_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Worker::Rebalance 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerRebalanceMain( void );
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __WORKER_REBALANCE_H__ */
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
// sample 소스 파일
|
||||
|
||||
#include ""
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
rc_balanced header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/07
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : huibong
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
|
||||
Redistribution and use in source and binary forms, with or with out
|
||||
modification, are not permitted in outside of Solbox Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __REBALANCE_DATA_H__
|
||||
#define __REBALANCE_DATA_H__
|
||||
|
||||
#include <>
|
||||
|
||||
|
||||
|
||||
// class 설명
|
||||
class C
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __REBALANCE_DATA_H__ */
|
||||
|
||||
Reference in New Issue
Block a user