Files
interactive/rcts/rc_syncd/src/SourceData.cpp
T
2026-08-07 17:38:18 +09:00

478 lines
12 KiB
C++

/***************************************************************************
Source Data
-----------------------------------------
begin : 2011/10/27
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 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 "SourceData.h"
#include "Database.h"
#include "DataFile.h"
#include "SyncList.h"
#include "ServiceConfig.h"
#include "RcdbInfo.h"
#include "Util.h"
#include "Logger.h"
#include <errno.h>
#include <string.h>
// 전체 자료량 구하지 못했을 때 해당 값 사용
#define MAX_LIMIT_OFFSET_COUNT 1000000
// 쿼리문에 사용할 임시 버퍼 Size
#define DEFAULT_BUFFER_SIZE 1024
CSourceData::CSourceData()
: m_bDELETED_YN(false)
, m_usedisplay(false)
, m_ismaster(false)
{
}
CSourceData::~CSourceData()
{
}
bool CSourceData::Init(bool master, CSyncInfo sourceParam)
{
bool r = false;
m_ismaster = master;
m_sourceParam = sourceParam;
if ( m_sourceParam.sync_type == SYNC_TYPE::NORMA_N_ONLY ||
m_sourceParam.sync_type == SYNC_TYPE::CHK_ONLY_N_ONLY )
m_bDELETED_YN = true;
else
m_bDELETED_YN = false;
m_strErrorMessage.clear();
if (m_ismaster)
r = m_dataFile.Init(m_sourceParam.file_master_name.c_str());
else
r = m_dataFile.Init(m_sourceParam.file_slave_name.c_str());
if (r == false)
{
m_strErrorMessage = "Failed Create source file.";
}
return r;
}
bool CSourceData::Execute()
{
string errmsg;
if (isError(errmsg) == true)
{
LOG(LERR, "Failed Initialization.");
return false;
}
ostringstream msg;
int nRet = ::pthread_create(&m_handleThread, NULL, CSourceData::Run, this);
if(nRet != 0)
{
msg << "Thread create failed.: errno: " << errno;
SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
return false;
}
return true;
}
void* CSourceData::Run(void* pVoid)
{
ostringstream msg;
CSourceData* pSourceData = reinterpret_cast<CSourceData*>(pVoid);
// pthread_detach( pthread_self() ); // 호출한 위치에서 pthread_join을 사용한다.
// 1. RCDB t_sms_sp_svc_product 정보를 조회하여 각 고객사별 svcsync count 정보를 가져와 저장처리한다.
// RCDB 접속 처리를 위한 객체 생성
DataBase* pPgSQL = new DataBase;
if( pPgSQL == NULL )
{
msg << "Cannot create DataBase object";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
return NULL;
}
CRcdbInfo info;
info.Load();
// RCDB 접속 시도 및 접속 실패시
if (pPgSQL->PgOpenDB(info.m_strRcdbIp, info.m_nRcdbPort, info.m_strRcdbName, info.m_strRcdbAcct, info.m_strRcdbAcctPw) == NULL)
{
if (pPgSQL != NULL)
{
msg << "RCDB Connect failed [" << pPgSQL->GetErrorMessage() << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
// 2014.06.07 dadamin
// 작업 대상 테이블명 가져오기
string tblname = pSourceData->GetTargetTable(pPgSQL);
if ( tblname.size() <= 0 )
{
if (pPgSQL != NULL)
{
string tranid = pSourceData->m_sourceParam.sync_slave;
if (pSourceData->m_ismaster) tranid = pSourceData->m_sourceParam.sync_master;
msg << "Failed to fetch Target table.[" << tranid << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
// 2014.08.06 dadamin
// 해당테이블 전체 자료량에 10% 값을 limit 값으로 사용한다.
int64_t total = pSourceData->GetRows(pPgSQL, tblname.c_str());
int64_t getlimit = (total > 0 ? int64_t(total*0.1) : MAX_LIMIT_OFFSET_COUNT);
getlimit = (getlimit == 0 ? MAX_LIMIT_OFFSET_COUNT : getlimit);
int nResult = 0;
// Query Resultset Clear.
pPgSQL->PgClear();
///////////////////////////////////////////////////////////////////////////////////////////////////
int nNowResult = 0;
string strResultUri, strResultType, strResultLength;
string strResultLastDate, strResultDeletedYN, strResultFilenameHash;
char* szResultUriTmp1 = NULL;
char* szResultHasNameTmp1 = NULL;
// Slave의 경우 파일이 없는 경우가 발생할 수 있으므로 미리 빈파일을 생성해 둔다.
pSourceData->m_dataFile.Write("");
do
{
ostringstream sql;
pPgSQL->PgClear();
// 2014.06.13 dadamin
// 신규 형상(9.3) 경우 display_name 사용하여 대소문자 기능을 지원하며,
// 기존 형상의 uri와 동일한 값을 가지고 있으므로 해당 형상인 경우
// uri 대신 display_name으로 소스 데이터를 가져오게 한다.
if(pSourceData->m_usedisplay)
sql << "SELECT display_name, resource_type, get_content_length, file_lastmodified, deleted_yn, filename_hash ";
else
sql << "SELECT uri, resource_type, get_content_length, file_lastmodified, deleted_yn, filename_hash ";
sql << "FROM " << tblname << " ";
sql << "WHERE get_lastmodified >= $1 AND get_lastmodified < $2 ";
sql << "AND depth >= 0 AND depth <= 1000 ";
sql << "AND uri LIKE '/' || $3 || '/%' ";
if( pSourceData->m_bDELETED_YN )
sql << "AND deleted_yn = 'N' ";
sql << "AND is_cache = 'N' ";
if(pSourceData->m_usedisplay)
sql << "ORDER BY display_name, get_lastmodified ";
else
sql << "ORDER BY uri, get_lastmodified ";
sql << "LIMIT $4 OFFSET $5 ";
#ifdef _DEBUG
if(nNowResult == 0)
cout << szQuery << endl;
#endif // _DEBUG
//
//MAX_LIMIT_OFFSET_COUNT
//nNowResult
const char *paramValues[5];
ostringstream tmp;
string strTime1, strTime2;
string strLimit, strOffSet;
tmp << str2longtime(pSourceData->m_sourceParam.start_time);
strTime1 = tmp.str(); tmp.str("");
tmp << str2longtime(pSourceData->m_sourceParam.end_time);
strTime2 = tmp.str(); tmp.str("");
tmp << getlimit;
strLimit = tmp.str(); tmp.str("");
tmp << nNowResult;
strOffSet = tmp.str(); tmp.str("");
paramValues[0] = strTime1.c_str();
paramValues[1] = strTime2.c_str();
if (pSourceData->m_ismaster)
paramValues[2] = pSourceData->m_sourceParam.sync_master.c_str();
else
paramValues[2] = pSourceData->m_sourceParam.sync_slave.c_str();
paramValues[3] = strLimit.c_str();
paramValues[4] = strOffSet.c_str();
nNowResult += getlimit;
// Query 실행
//pPgSQL->PgDoExec(szQuery);
pPgSQL->PgDoExecParams((char*) sql.str().c_str(), 5, paramValues);
// Query 결과를 가져온다.
nResult = pPgSQL->PgResult(DataBase::NOT_CLEAR);
if(nResult < 0)
{
pPgSQL->PgClear();
// RCDB t_dav_resource 테이블 조회 실패
msg << "RCDB Meta row get failed.[" << nResult << "][" << pPgSQL->GetErrorMessage() << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
break;
}
// 결과 Row 수 확인
nResult = pPgSQL->GetNoTuples();
// 조회 결과가 존재하는 경우 조회된 결과를 파일에 저장
for (int n = 0; n < nResult; n++)
{
szResultUriTmp1 = NULL;
szResultUriTmp1 = strchr(pPgSQL->GetValue(n, 0)+1, '/'); // URI (앞의 tran_id를 삭제한다.)
// skip 키워드 포함되어 있는 검사한다.
if(pSourceData->IsSkipKeyword(szResultUriTmp1) == true)
{
msg << "SKIP Source data." << szResultUriTmp1;
_LOG(LNOT, "%s", msg.str().c_str());
msg.str("");
//REPORT_SUCCESS("[%s] [SKIP] %s (source data has skip keyword.)",
// pSourceData->m_sourceParam.m_tranid.c_str(), szResultUriTmp1);
continue;
}
strResultUri = szResultUriTmp1;
strResultType = pPgSQL->GetValue(n, 1); // resource_type
strResultLength = pPgSQL->GetValue(n, 2); // get_content_length
strResultLastDate = pPgSQL->GetValue(n, 3); // file_lastmodified
strResultDeletedYN = pPgSQL->GetValue(n, 4); // deleted_yn
// 디렉토리 경우 hash가 없는 경우도 있으므로 파일만 hash 사용
szResultHasNameTmp1 = NULL;
if ( strResultType.compare("0") == 0 )
szResultHasNameTmp1 = strrchr(pPgSQL->GetValue(n, 5) + 1, '/'); // filename_hash ( 볼륨 정보 삭제함)
if (szResultHasNameTmp1)
strResultFilenameHash = szResultHasNameTmp1;
else
strResultFilenameHash = "";
if(strResultUri.size() <= 0) // URI가 없는 경우 저장 안함
continue;
if(strResultType.size() <= 0) // 파일 타입값이 NULL 일 경우 0으로 기본 셋팅
strResultType = "0";
if (strResultLastDate.size() <= 0) // 파일 크기값이 NULL 일 경우 0으로 기본 셋팅
strResultLastDate = "0";
// 해당 정보를 찾은 경우
pSourceData->m_dataFile.Write("%s|%s|%s|%s|%s|%s",
strResultUri.c_str(),
strResultFilenameHash.c_str(),
strResultType.c_str(),
strResultLength.c_str(),
strResultLastDate.c_str(),
strResultDeletedYN.c_str());
}
#ifdef _DEBUG
cout << "Generate tmp file : " << pSourceData->m_dataFile.GetFilePath()
<< "," << pSourceData->m_dataFile.GetFileName()
<< ", run count=" << nResult
<< " / " << nNowResult - MAX_LIMIT_OFFSET_COUNT
<< endl;
#endif // _DEBUG
} while (getlimit == nResult); // do while()
//////////////////////////////////////////////////////////////////////////////////////////////////
// DB Object 삭제 처리.
if( pPgSQL != NULL )
{
pPgSQL->PgClear();
pPgSQL->PgCloseDB();
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
bool CSourceData::IsSkipKeyword(string key)
{
bool r = false;
vector<string> skipwords = CServiceConfig::GetInstance()->GetSkipWord();
int keycnt = skipwords.size();
for( int i=0; i< keycnt; ++i )
{
string k = skipwords[i];
vector<string> v;
StringSplit(k, "|", v);
string::size_type pos;
pos = key.find(v[1]);
if ( pos != string::npos )
{
switch (v[0][0])
{
case 'h':
if (pos == 0)
{
if (v[1] == key || key.at(v[1].size()) == '/')
r = true;
}
break;
case 'm':
if(pos > 0 && key.size() > pos + v[1].size())
r = true;
break;
case 't':
if(pos > 0 && key.size() == pos + v[1].size())
r = true;
break;
default:
break;
}
}
}
return r;
}
// 2014.06.07 dadamin
// 작업 대상 테이블
string CSourceData::GetTargetTable(DataBase *d)
{
string r;
ostringstream msg;
if (d == NULL)
return "";
string sql = "SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource'";
d->PgDoExec(sql);
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
d->PgClear();
msg << "SetTargetTableType failed : " << d->GetErrorMessage();
LOG(LERR, msg.str().c_str());
return "";
}
if (d->GetNoTuples() > 0)
{
m_usedisplay = false;
r = "t_dav_resource";
}
else
{
m_usedisplay = true;
if (m_ismaster)
r = "t_meta_" + m_sourceParam.sync_master;
else
r = "t_meta_" + m_sourceParam.sync_slave;
}
d->PgClear();
msg << "Set Target Table : " << r;
LOG(LDBG, msg.str().c_str());
return r;
}
static int64_t convert(string &s)
{
if(s.size() == 0 )
return 0;
int64_t v = 0;
size_t i=0;
char sign = (s[0] == '-' || s[0] == '+') ? (++i, s[0]) : '+';
for( ; i < s.size(); ++i)
{
if ( s[i] < '0' || s[i] > '9' )
return 0;
v = v * 10 + s[i] - '0';
}
return sign == '-' ? -v : v;
}
// 해당 테이블 전체 rows
int64_t CSourceData::GetRows(DataBase *d, const char* tblname)
{
int64_t r = 0;
ostringstream msg;
// SELECT relname, reltuples from pg_class where relname ='t_meta_346';
if (d == NULL)
return 0;
ostringstream sql;
sql << "SELECT relname, reltuples::bigint from pg_class where relname = ";
sql << "'" << tblname << "'";
d->PgDoExec((char*)sql.str().c_str());
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
d->PgClear();
msg << "Get Table Rows failed : " << d->GetErrorMessage();
LOG(LERR, msg.str().c_str());
return 0;
}
if (d->GetNoTuples() > 0)
{
string t = d->GetValue(0, 1);
r = convert(t);
}
msg << "Table : " << tblname << ", Rows : "<< r;
LOG(LDBG, msg.str().c_str());
d->PgClear();
return r;
}