base
This commit is contained in:
@@ -0,0 +1,383 @@
|
||||
#include "ThreadTargetSelect.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 5
|
||||
#define SLEEP_TIME_MAX 10
|
||||
|
||||
|
||||
// DB 조회시 SELECT limit count
|
||||
// - queue 의 최소 크기가 2048 이므르.. DB 조회시 2000 씩만 조회하도록 한다.
|
||||
#define DB_SELECT_LIMIT 2000
|
||||
|
||||
|
||||
// Content select query 수행 시간이 지정된 시간 이상일 경우 로깅 처리 (sec)
|
||||
#define LOGGING_MAX_TIME_QUERY_EXEC 30
|
||||
|
||||
|
||||
CThreadTargetSelect::CThreadTargetSelect( CDataQueue<CDataAccess> * const pQueue, unsigned int uTargetService )
|
||||
: m_pQueue(pQueue), m_uTargetService(uTargetService)
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0;
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
m_ullResourceIdMin = 0;
|
||||
m_ullResourceIdMax = 0;
|
||||
m_ullResourceIdStart = 0;
|
||||
}
|
||||
|
||||
CThreadTargetSelect::~CThreadTargetSelect()
|
||||
{
|
||||
// 내부 therad 가 동작 중인 경우... 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
|
||||
// 혹시나 해서 정석대로 내부 변수 NLLL 처리
|
||||
if( m_pQueue != NULL )
|
||||
m_pQueue = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
bool CThreadTargetSelect::Start( void )
|
||||
{
|
||||
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueue == NULL )
|
||||
{
|
||||
LOG( LERR, "ThreadTargetSelect: content queue pointer is null." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CThreadTargetSelect::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패시
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "ThreadTargetSelect: thread create failed. [%d: %s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
|
||||
void * CThreadTargetSelect::threadFunc( void * arg )
|
||||
{
|
||||
CThreadTargetSelect * pObject = reinterpret_cast<CThreadTargetSelect *> ( arg );
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CThreadTargetSelect::Execute( void )
|
||||
{
|
||||
// sleep time 조정 처리
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
|
||||
// 전체 구간 scan 이 완료된 경우...
|
||||
if( m_ullResourceIdStart > m_ullResourceIdMax )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] content select completed. max sleep[%u]. resource_id min:%llu max:%llu next:%llu"
|
||||
, m_uSleep, m_ullResourceIdMin, m_ullResourceIdMax, m_ullResourceIdStart );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 조회 queue 상에 저장 가능 공간이 있는지 확인
|
||||
if( m_pQueue->GetFreeSize() < DB_SELECT_LIMIT )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] queue free size[%u / %u] too lower. select job skip and max sleep %u sec."
|
||||
, m_pQueue->GetFreeSize(), m_pQueue->GetMaxSize(), m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[SELECT ] start with target svc[%u], queue_free[%u]"
|
||||
, m_uTargetService, m_pQueue->GetFreeSize() );
|
||||
|
||||
|
||||
// DB 처리를 수행할 객체 생성
|
||||
// DB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase db;
|
||||
|
||||
// DB 와 연결을 시도한다.
|
||||
if( db.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, "[SELECT ] db connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// resource_id 정보를 추출한다.
|
||||
if( m_ullResourceIdMin == 0 && m_ullResourceIdMax == 0 )
|
||||
{
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// resource_id 정보 조회
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT MIN(resource_id), MAX(resource_id) FROM t_access_%u "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' ; "
|
||||
, m_uTargetService
|
||||
);
|
||||
|
||||
// Query 실행
|
||||
db.PgDoExec( szQuery );
|
||||
nResult = db.PgResult( DataBase::NOT_CLEAR );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] service[%u] resource_id min/max select error. [%d][%s][%s]"
|
||||
, m_uTargetService, nResult, db.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
|
||||
// DB 연결 해제
|
||||
db.PgCloseDB();
|
||||
|
||||
// 대부분 t_access_xxx table 이 존재하지 않는 경우 이므로...
|
||||
// 재시도 시간을 최대한 지연시킨다.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 추출된 정보 저장
|
||||
m_ullResourceIdMin = atoll( db.GetValue( 0, 0 ) );
|
||||
m_ullResourceIdMax = atoll( db.GetValue( 0, 1 ) );
|
||||
|
||||
// resource_id 정보 최초 추출시.. start 시점은 최소값으로 설정 처리
|
||||
m_ullResourceIdStart = m_ullResourceIdMin;
|
||||
|
||||
// db resultset clear.
|
||||
db.PgClear();
|
||||
|
||||
_LOG( LINF, "[SELECT ] service[%u] resource_id min: %llu max: %llu"
|
||||
, m_uTargetService, m_ullResourceIdMin, m_ullResourceIdMax );
|
||||
}
|
||||
|
||||
|
||||
// DB 에서 access time 추출 대상 content 목록을 조회하여 queue 에 저장 처리한다.
|
||||
SelectContent( db );
|
||||
|
||||
|
||||
// DB 와의 연결을 명시적으로 해제 처리
|
||||
db.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CThreadTargetSelect::SelectContent( DataBase & db )
|
||||
{
|
||||
// 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 );
|
||||
|
||||
|
||||
_LOG( LINF, "[SELECT ] service[%u] content select start with start resource_id: %llu"
|
||||
, m_uTargetService, m_ullResourceIdStart );
|
||||
|
||||
|
||||
// DB 연결은 이미 되어 있는 상태
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
|
||||
// 대상 content 조회
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT resource_id, uri, host_name, filename_hash "
|
||||
"FROM t_access_%u "
|
||||
"WHERE resource_id >= %llu AND resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' AND file_lastaccess = 0 "
|
||||
"ORDER BY resource_id ASC LIMIT %d"
|
||||
, m_uTargetService
|
||||
, m_ullResourceIdStart
|
||||
, DB_SELECT_LIMIT
|
||||
);
|
||||
|
||||
|
||||
// Query 실행
|
||||
db.PgDoExec( szQuery );
|
||||
nResult = db.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, db.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
db.PgClear();
|
||||
|
||||
// 재시도 시간 최대 설정
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int uPushTotalCount = 0;
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = db.GetNoTuples();
|
||||
|
||||
_LOG( LINF, "[SELECT ] content selected. [svc:%u select_count:%d ]"
|
||||
, m_uTargetService, nResult );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] content select tuple error. [%d][%s][%s]"
|
||||
, nResult, db.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
db.PgClear();
|
||||
|
||||
// 재시도 시간 최대 설정
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 적합한 Content 가 없는 경우..
|
||||
LOG( LWAR, "[SELECT ] content select not found. [%d][svc:%u][%s]"
|
||||
, nResult, m_uTargetService, szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
db.PgClear();
|
||||
|
||||
// 재시도 시간 최대 설정
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
// 더 이상 작업할 contnet 가 없으므로.. 더 이상 DB 조회 발생하지 않도록 처리
|
||||
m_ullResourceIdStart = m_ullResourceIdMax + 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// N 개의 조회 결과가 존재하는 경우
|
||||
// - 정해진 용량까지 Queue 에 push 처리한다.
|
||||
CDataAccess data;
|
||||
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
// 0: resouce_id
|
||||
// 1: uri
|
||||
// 2: host_name
|
||||
// 3: filename_hash
|
||||
|
||||
// 확인 결과 resouce_id 의 최대값은 9223372036854775807
|
||||
// long long int 의 최대값은 9223372036854775807 으로 resouce_id 값과 동일
|
||||
// 실제 변환 테스트해 보니 최대값까지 정상 변환 확인함.
|
||||
|
||||
data.m_uResourceId = atoll( db.GetValue( i, 0 ) );
|
||||
data.m_strUri = db.GetValue( i, 1 );
|
||||
data.m_strHostname = db.GetValue( i, 2 );
|
||||
data.m_strFilenamehash = db.GetValue( i, 3 );
|
||||
|
||||
// push 처리
|
||||
if( m_pQueue->Push( data ) == false )
|
||||
{
|
||||
// push 처리 실패시...
|
||||
// 2 sec 후 재시도해서 실패하면.. queue full 가능성이 있으므로...
|
||||
// 버리고.. loop 중지
|
||||
|
||||
sleep( 2 );
|
||||
if( m_pQueue->Push( data ) == false )
|
||||
{
|
||||
LOG( LWAR, "[SELECT ] content select ok. but queue push fail. [svc:%u resource_id:%llu]"
|
||||
, m_uTargetService, data.m_uResourceId );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// total push count 1 증가
|
||||
uPushTotalCount++;
|
||||
|
||||
// 로깅 for debug
|
||||
//_LOG( LINF, "[SELECT ] content select, push ok. [svc:%u resource_id:%llu]", m_uTargetService, data.m_uResourceId);
|
||||
}
|
||||
|
||||
// 다음 조회 start resouce_id 정보 설정
|
||||
m_ullResourceIdStart = data.m_uResourceId + 1;
|
||||
|
||||
}
|
||||
|
||||
db.PgClear();
|
||||
|
||||
// Content 선택 결과 로깅
|
||||
_LOG( LINF, "[SELECT ] content push end. service[%u] select[%d][%.2lf sec] push[%u] netxt start resource_id[%llu]"
|
||||
, m_uTargetService, nResult, timeElapsed, uPushTotalCount, m_ullResourceIdStart );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user