This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+383
View File
@@ -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;
}