base
This commit is contained in:
@@ -0,0 +1,477 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user