#include "ReplicateSelect.h"

#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "Logger.h"
#include "Database.h"
#include "ServiceConfig.h"
#include "Util.h"


// Replication 대상 조회시...
// 현재 기준 얼마 이전에 업로드, 변경된 Content 를 대상으로 할 것인지에 대한 최대, 최소값
// 기본적으로 conf 의 TARGET_SELECT_INTERVAL 설정값을 사용
// 단위 : second
#define MAX_TARGET_SELECT_INTERVAL		1800       // 30 분 이전 content 까지 대상으로 판단
#define MIN_TARGET_SELECT_INTERVAL	      60       //  1 분 이전 content 까지 대상으로 판단


// 프로세스 구동시 Replication 대상 선택 MAX, MIN 시간 ( hour 단위 )
#define MIN_TARGET_SELECT_INTERVAL_ON_STARTING		1 

// CHG 2014-07-16 huibong
// 팀 회의 결과.. replication target select 주기는 
// 기존의 복잡한 계산 로직 대신....고정값으로 sleep 처리토록 수정한다.
// - 짧은 sleep 은 DB 부하만 발생시키고..  queue 처리 등을 고려했을 때 큰 의미가 없다고 판단.

// Replicatin 처리시 매 Loop 마다 Sleep 시간 정보 정의 ( second 단위)
#define DEFAULT_SLEEP_TIME  60

// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시 
#define MAX_SLEEP_TIME      60


// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 1024

////////////////////////////////////////////

// 생성자
CReplicateSelect::CReplicateSelect( CDataQueue<CSourceData> * pQueue )
	: m_pSourceQueue( pQueue )
	  , m_timeLastCheck(0)
{
	SetTargetSelectInterval( MAX_TARGET_SELECT_INTERVAL );
	SetTargetSelectIntervalOnStarting( MIN_TARGET_SELECT_INTERVAL_ON_STARTING );
	SetSleepTimeDefault();

	m_threadHandle = 0;

	// NEW 2014-06-16 신규 멤버 변수 초기화 코드 추가.
	// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
	m_bServiceTableSeparate = false;
}

CReplicateSelect::~CReplicateSelect()
{
	// Thread 동작 정지 처리
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 
	//   업무 Flow 수정시 주의할 것
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );	
}

// 초기화 작업 수행
bool CReplicateSelect::Init()
{
	// 조회 정보를 저장할 Queue 포인터가 NULL 경우 오류 처리
	if( m_pSourceQueue == NULL )
	{
		LOG( LERR, "replication queue is null");
		return false;
	}
	
	// Queue 정보 초기화 처리.
	m_pSourceQueue->Init();

	// 2014-06-13 huibong
	// config 객체 변경 및 Skip Keyword 처리 관련 멤버 변수화에 따른 초기화 처리.
	m_vecSipkWord = CServiceConfig::GetInstance()->GetSkipWord();

	// RCDB 접속 정보 Load.
	m_rcdbInfo.Load();

	// 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 )
	{
		LOG( LERR, "RCDB connection failed.");
		return false;
	}
	else
	{
		// 접속 성공한 경우...
		LOG( LINF, "RCDB connection test ok..." );
				
		// NEW 2014-06-13 huibong
		// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
		// m_bServiceTableSeparate 변수 설정 처리.
		// 해당 변수 초기화는 생성자에서 1차로 수행함.
		char szQuery[1024];
		snprintf( szQuery, 1023,
			      "SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );

		rcdb.PgDoExec( szQuery );
		if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
		{
			// Query 수행 결과 오류 발생시...
			// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
			// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
			LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );

			rcdb.PgClear();
			rcdb.PgCloseDB();

			return false;
		}
		else
		{
			// Query 수행이 정상인 경우.
			int  nResult = rcdb.GetNoTuples();
			if( nResult > 0 )
			{
				// t_dav_resource 테이블이 존재하는 경우...
				m_bServiceTableSeparate = false;
				_LOG( LINF, "CReplicateSelect: t_dav_resource table exist.[Old RCDB Type]" );
			}
			else
			{
				// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
				m_bServiceTableSeparate = true;
				_LOG( LINF, "CReplicateSelect: t_dav_resource table not exist.[New RCDB Type]" );
			}
		}
		
		// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
		rcdb.PgClear();
		rcdb.PgCloseDB();

		return true;
	}
}

// Thread 를 생성하여 할당된 작업을 수행.
bool CReplicateSelect::Start()
{
	int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateSelect::threadFunc, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}

	return true;
}

bool CReplicateSelect::Start( unsigned int interval, unsigned int intervalOnStarting )
{
	SetTargetSelectInterval( interval );
	SetTargetSelectIntervalOnStarting( intervalOnStarting );

	return this->Start();
}

// Thread function
void * CReplicateSelect::threadFunc( void * arg )
{
	CReplicateSelect* pObject = reinterpret_cast<CReplicateSelect *>( arg );
	pthread_detach( pthread_self() );

	while( 1 )
	{
		pthread_testcancel();
		pObject->Execute();
		pthread_testcancel();

		sleep( pObject->m_timeSleep );       // 멤버변수에 설정된 시간만큼 Sleep 처리
	}

	// NEW 2014-06-13 huibong
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;

	// CHG 2014-06-13 huibong
	// detach 된 Thread 에 대해 pthread_exit() 호출시
	// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 발생 가능( FreeBSD ftsd 발생)
	// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
	//pthread_exit(NULL);
	return NULL;
}

// loop 단위의 replication 대상 조회 작업 수행
void CReplicateSelect::Execute()
{
	// DB 조회 처리 함수 호출
	SelectFromDB();

	// Logging
	_LOG( LINF, "Replication target select complete. last check time[%lld], sleep[%u] sec", m_timeLastCheck, m_timeSleep );
}


// RCDB 로 부터 Replication 대상 Content 를 조회한 후...
// 해당 정보를 Queue 상에 저장한다.
bool CReplicateSelect::SelectFromDB()
{
	// 1. 현재 시간정보를 구한다.( micro-second 단위)
	long long timeNow = longtime_now(); 
	if (timeNow < 0)
	{
		LOG( LERR, "longtime_now function error [%lld]", timeNow);
		return false;
	}

	// 2. RCDB 와 연결 처리.
	DataBase 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 )
	{
		LOG( LERR, "RCDB connection failed." );

		// DB 접속이 실패했으므로 Sleep Time 을 최대로 설정한다.		
		SetSleepTimeMax();
		return false;		
	}

	// Query 문장 저장을 위한 버퍼 생성
	char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
	// Query 수행 결과값 저장을 위한 변수..
	// - 최종 조회된 Replication 대상 content count 정보를 저장.
	int nResult = 0;

	// 3. t_sms_sp_svc_product 에서 각 고객사별 Replication count 정보를 가져온다.		
	snprintf(szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
			"SELECT sp_svc_tran_id, replication_count "
			"FROM t_sms_sp_svc_product "
			"WHERE replication_count > 0 "
			"ORDER BY sp_svc_tran_id desc "
			);

	// Query 실행 
	rcdb.PgDoExec(szQuery);
	nResult = rcdb.PgResult( DataBase::NOT_CLEAR );	
	if( nResult < 0 )
	{
		// RCDB t_sms_sp_svc_product 에 replication_count 컬럼이 존재하지 않는 경우
		// - Replication 처리를 당장 수행할 필요가 없으므로 루프 대기처리후 다음에 다시 조회처리.
		LOG( LERR, "RCDB t_sms_sp_svc_product table replication_count column not exist.[%s][%s]"
			, rcdb.GetErrorMessage().c_str(), szQuery );
		
		rcdb.PgClear();
		rcdb.PgCloseDB();

		// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
		// 또한 Sleep Time 역시 최대로 변경처리한다.
		m_timeLastCheck = timeNow;		
		SetSleepTimeMax();
		return true;
	}

	// 조회된 각 고객사별 replication 정보 확인
	nResult = rcdb.GetNoTuples();
	if( nResult <= 0 )
	{
		// Replication 을 수행할 고객사가 존재하지 않는 경우
		_LOG( LINF, "ReplicateTargetSelector: service not found that content replicate.[%d]"
			, nResult );
		
		rcdb.PgClear();
		rcdb.PgCloseDB();
		
		// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
		// 또한 Sleep Time 역시 최대로 변경처리한다.
		m_timeLastCheck = timeNow;		
		SetSleepTimeMax();
		return true;
	}

	// Replication 을 수행할 고객사가 존재하는 경우 멤버변수에 저장
	m_listCustomer.clear();
	for( int i =0 ; i< nResult; i++ )
	{
		// map 객체이므로.. key - value 형식으로 저장 처리.
		m_listCustomer[ rcdb.GetValue(i,0) ] = atoi( rcdb.GetValue(i,1));
	}

	// Query Resultset Clear.
	rcdb.PgClear();

	
	// 4. Replication 대상 파일을 조회한다.
	//    - 구형 RCDB 형상(m_bServiceTableSeparate == false) : t_dav_resouce 테이블에서 조회
	//    - 신형 RCDB 형상(m_bServiceTableSeparate == true ) : t_meta_tranid 테이블에서 조회
		
	// 조회 시작시간은 이전에 저장된 m_timeLastCheck 에서 지정된 제외시간 빼고
	// 조회 종료시간은 현재시간에서 지정된 제외시간만큼 뺀다.	
	long long timeSearchStart;
	long long timeSearchEnd;
	
	// 만약 최초 동작인 경우
	if( m_timeLastCheck == 0 )
	{
		// 현재시간으로부터 지정된 시간만큼 뺀 시간을 조회시작시간으로 설정.
		timeSearchStart = ( timeNow - GetTargetSelectIntervalOnStarting() );
	}
	else
	{
		// 이전 조회처리 시간에서 지정된 제외시간 만큼 뺀 시간을 시작시간으로 설정.		
		timeSearchStart = ( m_timeLastCheck - GetTargetSelectInterval() );
	}

	// 조회 종료시간은 현재시간에서 지정된 제외시간만큼 뺀다.	
	timeSearchEnd = ( timeNow - GetTargetSelectInterval() );


	// NEW 2014-07-16 huibong
	// 시간 동기화 관련 방어 코드 추가
	// - 잘못된 시간 동기화로 인해 조회 종료시간 <= 조회 시작시간 인 경우...
	// - Replication 대상 조회가 안돼 아래 Flow 의미가 없어지므로...  방어 코드 추가한다.
	if( timeSearchEnd <= timeSearchStart )
	{
		// 에러 정보 로깅 후..
		// RCDB 연결 해제 처리
		// 마지막 Check Time 은 
		// - 시간 동기화로 오류(실제 시간보다 빨라) 인해 복제가 발생하지 않았을 수 있으므로...
		// - 초기 기동 Flow 가 동작하도록 0 으로 설정 처리한다.
		// Sleep Time 최대로 변경처리한다.
		LOG( LERR, "Search end time[%lld] lower than start time[%lld]. check ntpdate."
			, timeSearchEnd, timeSearchStart );			

		rcdb.PgClear();
		rcdb.PgCloseDB();

		// 초기 기동 Flow 가 동작하도록 설정.
		m_timeLastCheck = 0;
		// Sleep time 은 최대로
		SetSleepTimeMax();

		return false;
	}

	/////////////////////////////////////////////////////////////////////////////////
	// NEW 2014-06-16 huibong
	// - RCDB 신규 형상 재설계에 따라...Meta 정보가 서비스별 Table 단위로 분리됨.
	// - 이에 기존 버전과 하위 호환성을 위해 t_dav_resource 테이블 존재 유무에 따라..
	// - replication 대상 content 조회 방식을 변경한다.

	// 구형 RCDB 형상인 경우.
	if( m_bServiceTableSeparate == false )
	{
		// t_dav_resource 에서 최신 Content 목록 조회 후
		// replication 대상 서비스인지 검사 후.
		// Queue 에 저장 처리.
		
		// Replication 대상 선택을 위한 Query 생성
		// CHG 2014-06-19 huibong
		// - 노경민 과장 요청에 의해 서브 query 상에서 uri 에 대한 DISTINCT 구문 제외

		// CHG 2015-03-26 huibong
		// - Cache 가 먼저 생성된 경우 cache 가 복제 대상으로 선택되는 버그 발견됨 (#21903)
		// - 이를 해결하기 위해 AND is_cache   = 'N' 조건절 추가함.

		snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
			"SELECT filename_hash, host_name, get_content_length, sp_svc_tran_id, uri "
			"FROM t_dav_resource "
			"WHERE uri IN "
			"   ( "
			"       SELECT uri FROM t_dav_resource  "
			"       WHERE   get_lastmodified >= %lld "
			"           AND get_lastmodified < %lld "
			"           AND deleted_yn  = 'N' "
			"           AND is_cache !='Y' "  // 캐시된 장비는 대상 선택에 영향을 미치면 안된다. 일시적인 복제증가이기 때문에 나중에 사라져버리기 때문이다.
			"       GROUP BY uri "
			"       HAVING COUNT(*) = 1 "     // including is_cache ='N|R' resut=1
			"   ) "
			"   AND deleted_yn = 'N' "        // 삭제된 content 는 제외.
			"   AND is_cache   = 'N' "        // 2015-03-26 #21903 cache 가 먼저 생성된 경우.. 제외 처리
			"   AND resource_type = 0 "       // 파일만.(0:file, 1:directory)
			"   AND get_content_length != 0 " // 업로드 시작시 content_length=0 으로 insert된다. 즉, 0인것은 업로드중으로 간주한다.
			"   ORDER BY uri, get_lastmodified desc; "
			, timeSearchStart, timeSearchEnd			
			);

		_LOG( LDBG, "CReplicateSelect: select t_dav_resource, time[%lld]~[%lld]", timeSearchStart, timeSearchEnd );

		// Query 실행 및 결과 확인
		rcdb.PgDoExec( szQuery );
		nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
		if( nResult < 0 )
		{
			// RCDB t_dav_resource 테이블 조회 실패시
			// Replication 처리를 당장 수행할 필요가 없으므로 
			// 루프 대기처리후 다음에 다시 조회처리.		
			LOG( LERR, "RCDB t_dav_resource replication content select failed.[%s][%s]"
				,  rcdb.GetErrorMessage().c_str(), szQuery );

			rcdb.PgClear();
			rcdb.PgCloseDB();

			// 마지막 실행시간은 이전 값을 사용하도록 설정하지 않는다.
			// 또한 Sleep Time 역시 최대로 변경처리한다.
			SetSleepTimeMax();
			return false;
		}

		// 결과 Row 수 확인
		nResult = rcdb.GetNoTuples();
		if( nResult < 0 )
		{
			// 오류 발생시
			LOG( LERR, "RCDB t_dav_resource GetNoTuples() error[%s][%s]"
				, rcdb.GetErrorMessage().c_str(), szQuery );

			rcdb.PgClear();
			rcdb.PgCloseDB();			

			// 마지막 실행시간은 이전 값을 사용하도록 설정하지 않는다.
			// 또한 Sleep Time 역시 최대로 변경처리한다.
			SetSleepTimeMax();
			return false;
		}
		else if( nResult == 0 )
		{
			// 조회 결과가 존재하지 않는 경우
			_LOG( LINF, "Replicaiton target content not exist. time[%lld]~[%lld]", timeSearchStart, timeSearchEnd );
			
			rcdb.PgClear();
			rcdb.PgCloseDB();
			
			// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
			// 또한 Sleep Time 역시 최대로 변경처리한다.
			m_timeLastCheck = timeNow;			
			SetSleepTimeMax();
			return true;
		}

		// 조회 결과가 존재하는 경우 조회된 결과를 Queue 에 저장	
		map< std::string, int >::iterator it;
		CSourceData resultData;
		int iPushCount = 0;
		std::string strTemp;
		for( int k = 0; k < nResult; k++ )
		{
			// 조회된 서비스 정보가 고객사 Replication List 상에 존재하는지 검사한다.
			strTemp = rcdb.GetValue( k, 3 );
			it = m_listCustomer.find( strTemp );

			// 해당 정보를 찾은 경우
			if( it != m_listCustomer.end() )
			{
				// 2012.09.24 : dadmain
				// 복제본 생성되지 않을 문자열이 포함되어 있는지 체크한다. 
				string uri = rcdb.GetValue( k, 4 );
				if( HasSkipKeyWord( uri ) == false )
				{
					resultData.m_szURI             = uri;
					resultData.m_szFileNameHash    = rcdb.GetValue( k, 0 );
					resultData.m_szOriginHostName  = rcdb.GetValue( k, 1 );
					resultData.m_nContentLength    = atoll( rcdb.GetValue( k, 2 ) );
					resultData.m_nReplicationCount = it->second;					

					// 2014-06-16 huibong
					// 신규 RCDB 형상 지원을 위해 service sequecn (구 tran_id)정보 추가 처리.
					resultData.m_szServiceSeq      = strTemp;
					
					// Queue 에 저장 처리.
					PushDataToSourceQueue( resultData );					
					iPushCount++;
				}
				else
				{
					// skip	
					_LOG( LWAR, "Replication selected. but URI has SIKP_KEYWROD.[%s]", uri.c_str() );
				}
			}
		}
		_LOG( LINF, "Replication target selected. total[%d], queue insert[%d]", nResult, iPushCount );		
	}
	else
	{
		// 서비스별 meta 테이블인 분리된 신형 RCDB 형상인 경우...
		// - 이미 조회된 replication 대상 고객사 table 에서만 조회 처리 후
		// - queue 에 저장 처리.
		map< std::string, int >::iterator it;
		std::string strServiceName;
		
		// nResult 값에 따라... sleep time 이 조절되므로....
		// 전체 조회 건수를 nResult 에 저장 처리해야 한다.
		nResult = 0;

		// 각 Query 수행 결과 저장을 위한 임시 변수. (nResult 변수 대치목적)
		int dbResult;
		CSourceData resultData;
		int iPushCount = 0;

		// Replication 대상 서비스별 Content 조회
		for( it = m_listCustomer.begin(); it != m_listCustomer.end(); ++it )
		{
			strServiceName    = it->first;			

			// Replication 대상 선택을 위한 Query 생성
			// CHG 2014-06-19 huibong
			// - 노경민 과장 요청에 의해 서브 query 상에서 uri 에 대한 DISTINCT 구문 제외

			// CHG 2015-03-26 huibong
			// - Cache 가 먼저 생성된 경우 cache 가 복제 대상으로 선택되는 버그 발견됨 (#21903)
			// - 이를 해결하기 위해 AND is_cache   = 'N' 조건절 추가함.

			snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
				"SELECT filename_hash, host_name, get_content_length, sp_svc_tran_id, uri "
				"FROM t_meta_%s "
				"WHERE uri IN "
				"   ( "
				"       SELECT uri FROM t_meta_%s  "
				"       WHERE   get_lastmodified >= %lld "
				"           AND get_lastmodified < %lld "
				"           AND deleted_yn  = 'N' "
				"           AND is_cache !='Y' "  // 캐시된 장비는 대상 선택에 영향을 미치면 안된다. 일시적인 복제증가이기 때문에 나중에 사라져버리기 때문이다.
				"       GROUP BY uri "
				"       HAVING COUNT(*) = 1 "     // including is_cache ='N|R' resut=1
				"   ) "
				"   AND deleted_yn = 'N' "        // 삭제된 content 는 제외.
				"   AND is_cache   = 'N' "        // 2015-03-26 #21903 cache 가 먼저 생성된 경우.. 제외 처리
				"   AND resource_type = 0 "       // 파일만.(0:file, 1:directory)
				"   AND get_content_length != 0 " // 업로드 시작시 content_length=0 으로 insert된다. 즉, 0인것은 업로드중으로 간주한다.
				"   ORDER BY uri, get_lastmodified desc; "
				, strServiceName.c_str(), strServiceName.c_str()
				, timeSearchStart, timeSearchEnd
				);
			
			_LOG( LDBG, "CReplicateSelect: select t_meta_%s, time[%lld]~[%lld]", strServiceName.c_str(), timeSearchStart, timeSearchEnd );

			// Query 실행 및 결과 확인
			rcdb.PgDoExec( szQuery );
			dbResult = rcdb.PgResult( DataBase::NOT_CLEAR );
			if( dbResult < 0 )
			{
				// RCDB t_meta_xxx 테이블 조회 실패시
				// 해당 서비스에 대해서는 Replication 처리를 당장 수행할 필요가 없다.				
				LOG( LERR, "RCDB t_meta_%s replication content select failed.[%s][%s]"
					, strServiceName.c_str(), rcdb.GetErrorMessage().c_str(), szQuery );

				rcdb.PgClear();

				// loop 계속 수행.
				continue;
			}

			// 결과 Row 수 확인 
			dbResult = rcdb.GetNoTuples();
			if( dbResult < 0 )
			{
				// 오류 발생시
				LOG( LERR, "RCDB t_meta_%s GetNoTuples() error[%s][%s]"
					, strServiceName.c_str(), rcdb.GetErrorMessage().c_str(), szQuery );

				// loop 계속 수행.
				rcdb.PgClear();
				continue;
			}
			else if( dbResult == 0 )
			{
				// 조회 결과가 존재하지 않는 경우
				//_LOG( LDBG, "Replication target content not exist. service[%s], time[%lld]~[%lld]"
				//	, strServiceName.c_str(), timeSearchStart, timeSearchEnd );

				// loop 계속 수행.
				rcdb.PgClear();
				continue;
			}
						
			// 조회 결과가 존재하는 경우 조회된 결과를 Queue 에 저장
			iPushCount = 0;
			for( int k = 0; k < dbResult; k++ )
			{
				// Skip keyword 에 포함되어 있는지 확인
				string uri = rcdb.GetValue( k, 4 );
				if( HasSkipKeyWord( uri ) == false )
				{
					resultData.m_szURI             = uri;
					resultData.m_szFileNameHash    = rcdb.GetValue( k, 0 );
					resultData.m_szOriginHostName  = rcdb.GetValue( k, 1 );
					resultData.m_nContentLength    = atoll( rcdb.GetValue( k, 2 ) );
					resultData.m_nReplicationCount = it->second;

					// 2014-06-16 huibong
					// 신규 RCDB 형상 지원을 위해 service sequecn (구 tran_id)정보 추가 처리.
					resultData.m_szServiceSeq = strServiceName;

					// Queue 에 저장 처리.
					PushDataToSourceQueue( resultData );
					iPushCount++;
				}
				else
				{
					// skip	
					_LOG( LWAR, "Replication selected. but URI has SIKP_KEYWROD.[%s][%s]"
						, strServiceName.c_str(), uri.c_str() );
				}
			}

			// 다음 Query loop 조회를 위해 RCDB 조회 결과 set 을 clear 처리
			rcdb.PgClear();

			// 로깅
			_LOG( LINF, "Replication target selected. service[%s], total[%d], queue insert[%d]"
				, strServiceName.c_str(), dbResult, iPushCount );
			
			// 조회 결과를 합산 처리.
			nResult += dbResult;
		}
	}

	//////////////////////////////////////////////////

	// RCDB 관련 정리 처리.
	rcdb.PgClear();
	rcdb.PgCloseDB();

	// 마지막 조회시간을 현재시간으로 설정한다.
	m_timeLastCheck = timeNow;
	
	// Sleep Time 설정.
	SetSleepTimeDefault();
	
	return true;
}


// loop 간 sleep time 을 MAX 값으로 설정.
void CReplicateSelect::SetSleepTimeMax()
{
	m_timeSleep = MAX_SLEEP_TIME;
	return;
}

// loop 간 sleep time 을 default 값으로 설정.
void CReplicateSelect::SetSleepTimeDefault()
{
	m_timeSleep = DEFAULT_SLEEP_TIME;
	return;
}


// replication 대상 선택시 시간 term 설정
void CReplicateSelect::SetTargetSelectInterval( unsigned int sec )
{
	if( sec > MAX_TARGET_SELECT_INTERVAL )
	{
		sec = MAX_TARGET_SELECT_INTERVAL;
		LOG( LWAR, "conf[TARGET_SELECT_INTERVAL] value[%u] too high. use default[%d]", sec, MAX_TARGET_SELECT_INTERVAL );
	}
	else if( sec < MIN_TARGET_SELECT_INTERVAL )
	{
		sec = MIN_TARGET_SELECT_INTERVAL;
		LOG( LWAR, "conf[TARGET_SELECT_INTERVAL] value[%u] too low. use default[%d]", sec, MIN_TARGET_SELECT_INTERVAL );
	}

	// BUG 2010-12-10 hjjang
	// int 형의 곱셈은 int 결과로 나오며..  이때 range overflow 가 발생하므로 
	// 아래와 같이 수정처리함.
	// sec * 1000000 :  micro-second 단위로 변환.
	m_nTargetSelectInterval = (long long)( sec * 1000000LL );

}

// 프로세스 구동시의 replication 대상 선택 시간 term 설정
void CReplicateSelect::SetTargetSelectIntervalOnStarting( unsigned int hour )
{
	if( hour < MIN_TARGET_SELECT_INTERVAL_ON_STARTING )
	{
		hour = MIN_TARGET_SELECT_INTERVAL_ON_STARTING;

		LOG( LWAR, "conf[TARGET_SELECT_INTERVAL_ON_STARTING] value[%u] too low. use default[%d]"
			, hour, MIN_TARGET_SELECT_INTERVAL_ON_STARTING );
	}

	// BUG 2010-12-09 huibong
	// int 형의 곱셈은 int 결과로 나오며..  이때 range overflow 가 발생하므로 
	// 아래와 같이 수정처리함.
	// hour * 3600 * 1000000 : micro-second 단위로 변환.
	m_nTargetSelectIntervalOnStarting = (long long)( hour * 3600LL * 1000000LL );
}

// micro second 단위로 설정된 replication 대상 선택 시간 term 을 반환
long long CReplicateSelect::GetTargetSelectInterval()
{
	return m_nTargetSelectInterval;
}

// micro second 단위로 설정된 프로세스 구동시의 replication 대상 선택 시간 term 을 반환
long long CReplicateSelect::GetTargetSelectIntervalOnStarting()
{
	return m_nTargetSelectIntervalOnStarting;
}

// 전달받은 data 를 멤버변수인 m_pSourceQueue 에 push 처리한다.
void CReplicateSelect::PushDataToSourceQueue( CSourceData& data )
{
	int errorCount = 0;
	while( m_pSourceQueue->Push( data ) == false )
	{
		if( ++errorCount == 3 )
		{
			LOG( LWAR, "data push into queue failed. retry. filenamehash[%s]", data.m_szFileNameHash.c_str() );
			errorCount = 0;
		}

		sleep( 1 ); // 1 초 마다 재시도
	}

	return;
}


/// 2012.09.24 : dadamin
/// @brief 해당 uri에 skip keyword가 포함되어있는지 체크함
/// @param [in] uri : 복제 대상 URI(복제 대상 full 파일명)
/// @return ture 시 해당 uri에 skip keyword를 포함
bool CReplicateSelect::HasSkipKeyWord( const std::string & uri )
{
	if( m_vecSipkWord.size() == 0 )
		return false;

	bool r = false;
	for( int i = 0; i< (int)m_vecSipkWord.size(); ++i )
	{
		string k = m_vecSipkWord[i];

		if( k.size() )
		{
			std::vector<string> v;
			StringSplit( k, "|", v, false );

			std::string::size_type pos;

			pos = uri.find( v[1] );

			if( pos != std::string::npos )
			{
				switch( v[0][0] )
				{
				case 'h':
				{
					// uri 형식이 /tran_id/path 이기 때문에 
					// head 위치는 2번째 '/' 위치와 동일핟.
					std::string::size_type towpos = uri.find( "/", 1 );

					if( towpos == pos )
					{
						r = true;
						LOG( LDBG, "Check Skip keyword HEAD- %s:%s", uri.c_str(), v[1].c_str() );
						break;
					}
				}
					break;
				case 'm':
					if( pos > 0 && uri.size() > pos + v[1].size() )
					{
						r = true;
						LOG( LDBG, "Check Skip keyword MIDDLE - %s:%s", uri.c_str(), v[1].c_str() );
						break;
					}
					break;
				case 't':
					if( pos > 0 && uri.size() == pos + v[1].size() )
					{
						r = true;
						LOG( LDBG, "Check Skip keyword TAIL - %s:%s", uri.c_str(), v[1].c_str() );
						break;
					}
					break;
				default:
					r = false;
					LOG( LWAR, "unknown find type. %s", uri.c_str() );
					break;
				}
				break;
			}
		}
	}
	return r;
}

void CReplicateSelect::StringSplit( std::string str, std::string delim, std::vector<std::string> &results, bool bUseEmpty /*= false*/ )
{
	const std::string strEmpty( "" );
	std::string::size_type cutAt;

	while( ( cutAt = str.find_first_of( delim ) ) != str.npos )
	{
		if( cutAt > 0 )
		{
			results.push_back( str.substr( 0, cutAt ) );
		}
		else
		{
			if( bUseEmpty && cutAt == 0 )
				results.push_back( strEmpty );
		}
		str = str.substr( cutAt + 1 );
	}
	if( str.length() > 0 )
	{
		results.push_back( str );
	}
}
