12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352 |
- #include <string.h>
- #include <malloc.h>
- #include <limits.h>
- #include <signal.h>
- #include <sys/stat.h>
- #include "fileutil.h"
- #include "strutil.h"
- #include "datalogger.h"
- #include "processclock.h"
- #include "debug.h"
- /////////////////////////////////////////////////////////////////////////////
- #define _USE_MODIFIED_INDEX 1
- #define _TRACK_TIME 1
- #define _FILE_SIZE_DELETE_MARGIN_PERCENT 10
- #define _SIZE_GUARD_TIMESTAMP_FILE_NAME "sguard.ts"
- #define _INVALID_TIMESTAMP_VALUE ((time_t)-1)
- /////////////////////////////////////////////////////////////////////////////
- CDataLogger::CDataLogger(LPCDLPARAMS pdlp, CLogfile &rlf) : m_lf(rlf),
- m_tidSGThread(0),
- m_bSGHasSizeLimitPrerequisites(false),
- m_bSGInProgress(false),
- m_bSGConfigured(false),
- m_nSGCurPassUTC(0),
- m_nSGLastPassUTC(0),
- m_condmtx1(PTHREAD_MUTEX_INITIALIZER),
- m_cond1(PTHREAD_COND_INITIALIZER),
- m_nLastLogTimestamp(0),
- m_bBadDateLogsDetected(false)
- {
- memset(&m_dlp, 0, sizeof(m_dlp));
- memset(&m_gv, 0, sizeof(m_gv));
- memset(&m_mtx, 0, sizeof(m_mtx));
- memset(&m_mutexAttr, 0, sizeof(m_mutexAttr));
- if(pdlp)
- memcpy(&m_dlp, pdlp, sizeof(m_dlp));
- if(!(m_bSGConfigured = !!m_dlp.nMaxSize || !!m_dlp.nMaxAge))
- m_lf.Error("Size guard not configured.\n");
- if(m_dlp.pszBaseDir)
- strncpy(m_szAppDir, m_dlp.pszBaseDir, sizeof(m_szAppDir) - 1);
- else
- ::GetAppDirectory(m_szAppDir, sizeof(m_szAppDir));
- ::pthread_mutexattr_init(&m_mutexAttr);
- ::pthread_mutexattr_settype(&m_mutexAttr, PTHREAD_MUTEX_RECURSIVE);
- ::pthread_mutex_init(&m_mtx, &m_mutexAttr);
- }
- /////////////////////////////////////////////////////////////////////////////
- CDataLogger::~CDataLogger(void)
- {
- Release();
- ::pthread_mutex_destroy(&m_mtx);
- ::pthread_mutexattr_destroy(&m_mutexAttr);
- ::pthread_cond_destroy(&m_cond1);
- }
- /////////////////////////////////////////////////////////////////////////////
- void CDataLogger::Release(void)
- {
- if(m_tidSGThread)
- {
- ::pthread_mutex_lock(&m_condmtx1);
- bool bSgInProg = m_bSGInProgress;
- ::pthread_mutex_unlock(&m_condmtx1);
- if(bSgInProg)
- CLogfile::StdErr("\nDatalogger is terminating. This may take some time ...\nPlease wait, before powering off the device!\n");
- Lock();
- m_lf.Lock();
- ::pthread_cancel(m_tidSGThread);
- m_lf.Unlock();
- Unlock();
- ::pthread_join(m_tidSGThread, NULL);
- m_tidSGThread = 0;
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- unsigned long CDataLogger::GetTagID(const char *pszVarPath, int nDataType, int nLogType)
- {
- CMySqlDB db;
- const char *pszID = NULL;
- char *pszEndptr;
- unsigned long nID = ULONG_MAX;
- std::string sSql;
- sSql = formatString("select `tagid` from `%s` where `path` = '%s' and `dataType` = %d and `logType` = %d", m_dlp.szTagsTable, pszVarPath, nDataType, nLogType);
- if(!db.Connect("localhost", m_dlp.szDBUser, m_dlp.szDBPass, m_dlp.szDBName))
- {
- m_lf.Error("CDataLogger::GetTagID: DB Error: %s\n", db.LastError().c_str());
- return nID;
- }
- CMySqlResult res = db.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::GetTagID: DB Error: %s\n", db.LastError().c_str());
- else
- {
- my_ulonglong nCount = res.RowCount();
- if(nCount > 0)
- {
- MYSQL_ROW pRow = res.FetchRow();
- pszID = pRow[0];
- nID = strtoul(pszID, &pszEndptr, 10);
- }
- else
- {
- sSql = formatString("insert into `%s` (`dataType`, `logType`, `path`) values(%d, %d, '%s')", m_dlp.szTagsTable, nDataType, nLogType, pszVarPath);
- res = db.Query(sSql.c_str());
- bError = res.error();
- if(bError)
- m_lf.Error("DB Error: %s\n", db.LastError().c_str());
- else
- {
- sSql = formatString("select `tagid` from `%s` where `path`='%s'", m_dlp.szTagsTable, pszVarPath);
- CMySqlResult res = db.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("DB Error: %s\n", db.LastError().c_str());
- else if(res.RowCount() > 0)
- {
- MYSQL_ROW pRow = res.FetchRow();
- pszID = pRow[0];
- nID = strtoul(pszID, &pszEndptr, 10);
- }
- }
- }
- }
- return nID;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::TableFileExists(const char *pszTableName)
- {
- char szDataFile[PATH_MAX];
- sprintf(szDataFile, "%s%s/%s.ibd", m_gv.szDataDir, m_dlp.szDBName, pszTableName);
- return !!FileExist(szDataFile);
- }
- /////////////////////////////////////////////////////////////////////////////
- int64_t CDataLogger::TableFileSize(const char *pszTableName)
- {
- struct stat statbuf;
- char szDataFile[PATH_MAX];
- sprintf(szDataFile, "%s%s/%s.ibd", m_gv.szDataDir, m_dlp.szDBName, pszTableName);
- if(stat(szDataFile, &statbuf) == -1)
- return 0;
- return (int64_t)statbuf.st_size;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::QueryServerVariable(CMySqlDB &rdb, const char *pszVarname, CMySqlVar &val)
- {
- if(!pszVarname || !*pszVarname)
- {
- m_lf.Error("CDataLogger::QueryServerVariable: No Variable name!\n");
- return false;
- }
- bool bError;
- std::string sSql;
- sSql = formatString("select @@%s;", pszVarname);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bError = res.error();
- if(!bError)
- {
- bError = true;
- do
- {
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- if(nRowCount != 1 || nFldCount != 1 || !pFields)
- break;
- MYSQL_ROW pRow = res.FetchRow();
- if(!pRow || !*pRow || !**pRow)
- break;
- bError = !val.FromField(pFields[0], pRow[0]);
- }
- while(false);
- }
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- #if 0
- bool CDataLogger::QueryStatusVariable(CMySqlDB &rdb, const char *pszVarname, CMySqlVar &val)
- {
- if(!pszVarname || !*pszVarname)
- {
- m_lf.Error("CDataLogger::QueryStatusVariable: No Variable name!\n");
- return false;
- }
- bool bError;
- std::string sSql;
- sSql = formatString("show session status like '%s'", pszVarname);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bError = res.error();
- if(!bError)
- {
- bError = true;
- do
- {
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- if(nRowCount != 1 || nFldCount != 2 || !pFields)
- break;
- MYSQL_ROW pRow = res.FetchRow();
- if(!pRow || !pRow[1] || !*pRow[1])
- break;
- bError = !val.FromField(pFields[1], pRow[1]);
- }
- while(false);
- }
- return !bError;
- }
- #endif
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::ReadGlobalOptions(CMySqlDB &rdb)
- {
- CMySqlVar val;
- memset(&m_gv, 0, sizeof(m_gv));
- if(QueryServerVariable(rdb, "datadir", val))
- {
- memset(m_gv.szDataDir, 0, sizeof(m_gv.szDataDir));
- if(!val.IsNull())
- val.CopyStrVal(m_gv.szDataDir, sizeof(m_gv.szDataDir) - 1, 0);
- }
- if(QueryServerVariable(rdb, "innodb_file_per_table", val))
- {
- if(!val.IsNull())
- m_gv.bInnoDbFilePerTable = val;
- }
- if(QueryServerVariable(rdb, "innodb_strict_mode", val))
- {
- if(!val.IsNull())
- m_gv.bInnoDbIsStrictMode = val;
- }
- if(QueryServerVariable(rdb, "innodb_file_format", val))
- {
- memset(m_gv.szInnoDbFileFormat, 0, sizeof(m_gv.szInnoDbFileFormat));
- if(!val.IsNull())
- {
- val.CopyStrVal(m_gv.szInnoDbFileFormat, sizeof(m_gv.szInnoDbFileFormat) - 1, 0);
- m_gv.bInnoDbIsBarracuda = !strcasecmp(m_gv.szInnoDbFileFormat, "barracuda");
- }
- }
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- void CDataLogger::SizeGuardTrigger(time_t ts)
- {
- if(m_bSGConfigured && m_tidSGThread)
- {
- ::pthread_mutex_lock(&m_condmtx1);
- if( !m_bSGInProgress &&
- !SizeGuardDayWorkDone(ts))
- {
- unsigned long long nMnUTC = (unsigned long long)_MIDNIGHT_TIMESTAMP_UTC(ts);
- if(SizeGuardLastPassRead() != nMnUTC)
- {
- m_nSGCurPassUTC = ts;
- ::pthread_cond_signal(&m_cond1);
- }
- else
- m_nSGLastPassUTC = nMnUTC;
- }
- ::pthread_mutex_unlock(&m_condmtx1);
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- #if 0
- bool CDataLogger::QueryTableSizes(CMySqlDB &rdb, const char *pszTableName)
- {
- bool bError;
- char *pszEndptr;
- MYSQL_ROW pRow;
- uint64_t nDataLength = 0, nDataFree = 0, nIndexLength = 0, nFreeExtents, nTotalExtents;
- std::string sSql;
- sSql = formatString("select `DATA_LENGTH`, `INDEX_LENGTH`, `DATA_FREE` from `information_schema`.`TABLES` where (`TABLE_SCHEMA` = '%s') AND (`TABLE_NAME` = '%s');", m_dlp.szDBName, pszTableName);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bError = res.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::QueryTableSizes: DB Error: %s\n", rdb.LastError().c_str());
- return false;
- }
- pRow = res.FetchRow();
- nDataLength = strtoull(pRow[0], &pszEndptr, 10);
- nIndexLength = strtoull(pRow[1], &pszEndptr, 10);
- nDataFree = strtoull(pRow[2], &pszEndptr, 10);
- //////////////////////////////////////////////////////////////////////////////////////////////
- sSql = formatString("select `FREE_EXTENTS`, `TOTAL_EXTENTS`, `DATA_FREE` from `information_schema`.`FILES` where `FILE_NAME` like '%%demo/%s.ibd';", pszTableName);
- CMySqlResult res2 = rdb.Query(sSql.c_str());
- bError = res2.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::QueryTableSizes: DB Error: %s\n", rdb.LastError().c_str());
- return false;
- }
- pRow = res2.FetchRow();
- nFreeExtents = strtoull(pRow[0], &pszEndptr, 10);
- nTotalExtents = strtoull(pRow[1], &pszEndptr, 10);
- int64_t nFileSize = TableFileSize(pszTableName);
- m_lf.Info("Data length: %ju, Data free: %ju, Total extents: %ju, Free extents: %ju, File size: %ju.\n", nDataLength + nIndexLength, nDataFree, nTotalExtents, nFreeExtents, nFileSize);
- TRACE("Data length: %ju, Data free: %ju, Total extents: %ju, Free extents: %ju, File size: %ju.\n", nDataLength + nIndexLength, nDataFree, nTotalExtents, nFreeExtents, nFileSize);
- return true;
- }
- #endif
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::CheckTable(CMySqlDB &rdb, const char *pszTableName, bool &bExists, bool &bUseResortTable, bool &bIsInnoDB)
- {
- bool bError;
- std::string sSql;
- const char *pszEngine = NULL;
- uint64_t nDataLength = 0, nIndexLength = 0;
- int64_t nFileSize = -1;
- bExists = bUseResortTable = false;
- bIsInnoDB = false;
- sSql = formatString("select `ENGINE`, `DATA_LENGTH`, `INDEX_LENGTH` from `information_schema`.`TABLES` where (`TABLE_SCHEMA` = '%s') AND (`TABLE_NAME` = '%s')", m_dlp.szDBName, pszTableName);
- CMySqlResult res1 = rdb.Query(sSql.c_str());
- bError = res1.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::CheckTables: DB Error: %s\n", rdb.LastError().c_str());
- return false;
- }
- else
- {
- my_ulonglong nCount = res1.RowCount();
- if(nCount > 0)
- {
- MYSQL_ROW pRow = res1.FetchRow();
- unsigned int nFc = res1.FieldCount();
- const MYSQL_FIELD *pFields = res1.FetchFields();
- CMySqlVar vals[3];
- bExists = true;
- for(unsigned int i = 0; i < nFc; i++)
- {
- vals[i].FromField(pFields[i], pRow[i]);
- if(!vals[i].FieldnameCmp("ENGINE"))
- {
- pszEngine = vals[i].StrVal();
- bIsInnoDB = !strcasecmp(pszEngine, "InnoDB");
- }
- else if(!vals[i].FieldnameCmp("DATA_LENGTH"))
- nDataLength = (uint64_t)vals[i];
- else if(!vals[i].FieldnameCmp("INDEX_LENGTH"))
- nIndexLength = (uint64_t)vals[i];
- }
- if(bIsInnoDB && m_gv.bInnoDbFilePerTable)
- {
- if(!TableFileExists(pszTableName))
- m_lf.Warning("'innodb_file_per_table' configured, but table '%s' does not have a data file!\n", pszTableName);
- else
- nFileSize = TableFileSize(pszTableName);
- }
- if(nFileSize >= 0)
- m_lf.Info("Found info on table '%s': Engine: '%s', Data size: %s, Index size: %s, File size: %s\n", pszTableName, pszEngine ? pszEngine : "unknown", strFormatByteSize(nDataLength, 1).c_str(), strFormatByteSize(nIndexLength, 1).c_str(), strFormatByteSize(nFileSize, 1).c_str());
- else
- m_lf.Info("Found info on table '%s': Engine: '%s', Data size: %s, Index size: %s\n", pszTableName, pszEngine ? pszEngine : "unknown", strFormatByteSize(nDataLength, 1).c_str(), strFormatByteSize(nIndexLength, 1).c_str());
- if(!pszEngine)
- m_lf.Warning("Failed to determine the Database engine of Table '%s'!\n", pszTableName);
- else if(strcmp(pszEngine, _DL_DATABSE_ENGINE_NAME))
- m_lf.Warning("Current Database engine of Table '%s' is '%s'! Please consider to migrate to '%s'!\n", pszTableName, pszEngine, _DL_DATABSE_ENGINE_NAME);
- sSql = formatString("select * from `%s` limit 0, 10", pszTableName);
- CMySqlResult res2 = rdb.Query(sSql.c_str()); // try to query table
- if(res2.error()) // if the query results in an error, the table may be corrupt!
- {
- char szTblName[256];
- m_lf.Error("Failed to query table '%s': %s\n", pszTableName, rdb.LastError().c_str());
- // if the table is corrupt, try to rename it and create a new instane of the original table.
- sprintf(szTblName, "%s_corrupt_%08zX", pszTableName, (size_t)time(NULL));
- sSql = formatString("rename table `%s` to `%s`", pszTableName, szTblName);
- CMySqlResult res3 = rdb.Query(sSql.c_str()); // try to rename the corrupt table
- if(res3.error())
- {
- // if the renaming fails, try to create a new working table with a different name
- m_lf.Error("Failed to rename table '%s': %s\n", pszTableName, rdb.LastError().c_str());
- bExists = false;
- bUseResortTable = true;
- }
- else
- {
- m_lf.Warning("Renamed table '%s' to '%s'\n", pszTableName, szTblName);
- bExists = false;
- }
- }
- }
- }
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::InitDatabase(bool bEnforceCreate)
- {
- CMySqlDB db;
- time_t nMaxLogTimestamp = m_nLastLogTimestamp = 0;
- bool bExists, bUseResortTable, bDummy;
- m_lf.Info("Connecting to Database server @ 'localhost'.\n");
- if(!db.Connect("localhost", m_dlp.szDBUser, m_dlp.szDBPass, NULL))
- {
- m_lf.Error("CDataLogger::InitDatabase: DB Error: %s\n", db.LastError().c_str());
- return false;
- }
- m_lf.Info("Success!\n");
- if(!ReadGlobalOptions(db))
- return false;
- if(!m_gv.bInnoDbIsBarracuda || !m_gv.bInnoDbFilePerTable || !m_gv.bInnoDbIsStrictMode)
- m_lf.Warning("InnoDB file format: '%s', InnoDB table space: '%s', InnoDB strict mode: %s.\n", m_gv.szInnoDbFileFormat, m_gv.bInnoDbFilePerTable ? "File per table" : "System", m_gv.bInnoDbIsStrictMode ? "yes" : "no");
- else
- m_lf.Info("InnoDB file format: '%s', InnoDB table space: '%s', InnoDB strict mode: %s.\n", m_gv.szInnoDbFileFormat, m_gv.bInnoDbFilePerTable ? "File per table" : "System", m_gv.bInnoDbIsStrictMode ? "yes" : "no");
- if(!CreateDatabase(db, bEnforceCreate))
- return false;
- m_lf.Info("Opening Database '%s'.\n", m_dlp.szDBName);
- if(db.SelectDB(m_dlp.szDBName))
- {
- m_lf.Error("CDataLogger::InitDatabase: DB Error: %s\n", db.LastError().c_str());
- return false;
- }
- m_lf.Info("Success!\n");
- if(!CheckTable(db, m_dlp.szTagsTable, bExists, bUseResortTable, bDummy))
- return false;
- if(!bExists)
- {
- if(bUseResortTable)
- {
- char szOrigTable[_DL_MAX_TABLE_NAME_LENGTH];
- memcpy(szOrigTable, m_dlp.szTagsTable, sizeof(szOrigTable));
- sprintf(m_dlp.szTagsTable, "%s_resort_%08zX", szOrigTable, (size_t)time(NULL));
- m_lf.Warning("Resorting to work-around table '%s'.\n", m_dlp.szTagsTable);
- }
- if(!CreateTagsTable(db))
- return false;
- }
-
- if(!AlterTagsTable(db)) // 23.10.2020, extend logtype enum if not up to date
- return false;
- if(!CheckTable(db, m_dlp.szLogsTable, bExists, bUseResortTable, m_gv.bLogsTblIsInnoDB))
- return false;
- if(!bExists)
- {
- if(bUseResortTable)
- {
- char szOrigTable[_DL_MAX_TABLE_NAME_LENGTH];
- memcpy(szOrigTable, m_dlp.szLogsTable, sizeof(szOrigTable));
- sprintf(m_dlp.szLogsTable, "%s_resort_%08zX", szOrigTable, (size_t)time(NULL));
- m_lf.Warning("Resorting to work-around table '%s'.\n", m_dlp.szLogsTable);
- }
- if(!CreateLogsTable(db))
- return false;
- #if _DL_DATABSE_ENGINE == _DL_DATABASE_ENGINE_INNODB
- m_gv.bLogsTblIsInnoDB = true;
- #endif // _DL_DATABSE_ENGINE == _DL_DATABASE_ENGINE_INNODB
- }
- if(m_bSGConfigured)
- {
- if(!(m_bSGHasSizeLimitPrerequisites = m_gv.bLogsTblIsInnoDB && m_gv.bInnoDbFilePerTable && TableFileExists(m_dlp.szLogsTable)))
- {
- m_lf.Warning("SG: Database configuration: InnoDB: %s, File-per-table tablespace: %s\n", m_gv.bLogsTblIsInnoDB ? "yes" : "no", m_gv.bInnoDbFilePerTable ? "yes" : "no");
- m_lf.Warning("SG: The current database configuration does not support monitoring of size limits. Size guard will operate on age limits only!\n");
- }
- ::pthread_mutex_lock(&m_condmtx1);
- if(::pthread_create(&m_tidSGThread, NULL, &CDataLogger::SizeGuardWorker, reinterpret_cast<void*>(this)))
- {
- m_tidSGThread = 0;
- m_lf.Error("SG: Failed to start size monitoring thread!\n");
- ::pthread_mutex_unlock(&m_condmtx1);
- return false;
- }
- ::pthread_cond_wait(&m_cond1, &m_condmtx1);
- ::pthread_mutex_unlock(&m_condmtx1);
- }
- if(!CreateLogsBDTable(db))
- return false;
- if(((nMaxLogTimestamp = GetLastLogTimestamp(db)) != _INVALID_TIMESTAMP_VALUE))
- m_nLastLogTimestamp = nMaxLogTimestamp;
- m_lf.Info("Database initialization complete.\n");
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::CreateDatabase(CMySqlDB &rdb, bool bEnforceCreate)
- {
- bool bError = false;
- std::string sSql;
- if(bEnforceCreate)
- {
- sSql = formatString("drop database if exists `%s`", m_dlp.szDBName);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::InitDatabase: DB Error: %s\n", rdb.LastError().c_str());
- }
- if(!bError)
- {
- sSql = formatString("create database if not exists `%s`", m_dlp.szDBName);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::InitDatabase: DB Error: %s\n", rdb.LastError().c_str());
- }
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::CreateTagsTable(CMySqlDB &rdb)
- {
- std::string sSql;
- const char *pszFormat =
- "CREATE TABLE IF NOT EXISTS `%s` (" \
- " `tagid` smallint(6) unsigned NOT NULL AUTO_INCREMENT," \
- " `dataType` enum('bool', 'I1', 'UI1', 'I2', 'UI2', 'I4', 'UI4', 'I8', 'UI8', 'float', 'double', 'string') NOT NULL," \
- " `logType` enum('IC', 'IU', 'VC', 'VU', 'ICR', 'IUR', 'VCR', 'VUR') NOT NULL," \
- " `path` varchar(%u) NOT NULL," \
- " PRIMARY KEY (`tagid`, `dataType`, `logType`)," \
- " KEY `path` (`path`)" \
- ") ENGINE=%s DEFAULT CHARSET=ascii";
- m_lf.Info("Creating Table '%s' (if not exists) using engine '%s'.\n", m_dlp.szTagsTable, _DL_DATABSE_ENGINE_NAME);
- sSql = formatString(pszFormat, m_dlp.szTagsTable, _DL_MAX_VARPATH_LENGTH, _DL_DATABSE_ENGINE_NAME);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::CreateTagsTable: DB Error: %s\n", rdb.LastError().c_str());
- else
- m_lf.Info("Success!\n");
- return !bError;
- }
- bool CDataLogger::AlterTagsTable(CMySqlDB &rdb)
- {
- std::string sSql = formatString("ALTER TABLE `%s` MODIFY COLUMN `logType` enum('IC','IU','VC','VU','ICR','IUR','VCR','VUR') NOT NULL", m_dlp.szTagsTable);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::AlterTagsTable: DB Error: %s\n", rdb.LastError().c_str());
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::CreateLogsTable(CMySqlDB &rdb)
- {
- std::string sSql;
- const char *pszFormat =
- #if _USE_MODIFIED_INDEX
- "CREATE TABLE IF NOT EXISTS `%s` (" \
- " `tagid` smallint(6) unsigned NOT NULL," \
- " `tslog` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," \
- " `value` double," \
- " `valueMin` double DEFAULT NULL," \
- " `valueMax` double DEFAULT NULL," \
- " PRIMARY KEY (`tslog`, `tagid`)," \
- " KEY `tagid` (`tagid`)" \
- ") ENGINE=%s DEFAULT CHARSET=ascii ROW_FORMAT=COMPRESSED";
- #else // _USE_MODIFIED_INDEX
- "CREATE TABLE IF NOT EXISTS `%s` (" \
- " `tagid` smallint(6) unsigned NOT NULL," \
- " `tslog` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," \
- " `value` double," \
- " `valueMin` double DEFAULT NULL," \
- " `valueMax` double DEFAULT NULL," \
- " PRIMARY KEY (`tagid`, `tslog`)," \
- " KEY `tslog` (`tslog`)" \
- ") ENGINE=%s DEFAULT CHARSET=ascii ROW_FORMAT=COMPRESSED";
- #endif // _USE_MODIFIED_INDEX
- m_lf.Info("Creating Table '%s' if not exists using engine '%s'.\n", m_dlp.szLogsTable, _DL_DATABSE_ENGINE_NAME);
- sSql = formatString(pszFormat, m_dlp.szLogsTable, _DL_DATABSE_ENGINE_NAME);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::CreateLogsTable: DB Error: %s\n", rdb.LastError().c_str());
- else
- m_lf.Info("Success!\n");
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::CreateLogsBDTable(CMySqlDB &rdb)
- {
- std::string sSql;
- const char *pszFormat =
- "CREATE TABLE IF NOT EXISTS `%s` (" \
- " `id` int unsigned unsigned NOT NULL AUTO_INCREMENT," \
- " `tagid` smallint(6) unsigned NOT NULL," \
- " `tslog` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," \
- " `value` double," \
- " `valueMin` double DEFAULT NULL," \
- " `valueMax` double DEFAULT NULL," \
- " PRIMARY KEY (`id`)," \
- " KEY `tslog` (`tslog`)," \
- " KEY `tagid` (`tagid`)" \
- ") ENGINE=%s DEFAULT CHARSET=ascii ROW_FORMAT=COMPRESSED";
- m_lf.Info("Creating Table '%s' if not exists using engine '%s'.\n", m_dlp.szLogsTableBD, _DL_DATABSE_ENGINE_NAME);
- sSql = formatString(pszFormat, m_dlp.szLogsTableBD, _DL_DATABSE_ENGINE_NAME);
- CMySqlResult res = rdb.Query(sSql.c_str());
- bool bError = res.error();
- if(bError)
- m_lf.Error("CDataLogger::CreateLogsBDTable: DB Error: %s\n", rdb.LastError().c_str());
- else
- m_lf.Info("Success!\n");
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::Log(unsigned long nTagID, double fValue, double fMin, double fMax, time_t nTimestamp, int nIndex, LogTypes lt, bool bNull, bool bNoBadDateCheck)
- {
- DL_LOG_ENTRY log;
- log.nTagID = nTagID;
- log.nTimestamp = nTimestamp;
- log.fValue = fValue;
- log.fMin = fMin;
- log.fMax = fMax;
- log.nIndex = nIndex;
- log.lt = lt;
- log.bNull = bNull;
- if(bNoBadDateCheck || (nTimestamp > m_nLastLogTimestamp))
- m_logs.push_back(log);
- else
- m_logsBD.push_back(log);
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- time_t CDataLogger::GetLastLogTimestamp(CMySqlDB &rdb)
- {
- time_t nTs = _INVALID_TIMESTAMP_VALUE;
- std::string sSql;
- do
- {
- sSql = formatString("select unix_timestamp(max(`tslog`)) from `%s`", m_dlp.szLogsTable);
- CMySqlResult res = rdb.Query(sSql.c_str());
- if(res.error())
- {
- m_lf.Error("CDataLogger::GetLastLogTimestamp: DB Error: %s\n", rdb.LastError().c_str());
- break;
- }
- else
- {
- CMySqlVar val;
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- if(nRowCount != 1 || nFldCount != 1 || !pFields)
- {
- // m_lf.Error("CDataLogger::GetLastLogTimestamp: Unexpected error!\n");
- break;
- }
- MYSQL_ROW pRow = res.FetchRow();
- if( !pRow ||
- !val.FromField(pFields[0], pRow[0]))
- {
- // m_lf.Error("CDataLogger::GetLastLogTimestamp: Unexpected error!\n");
- break;
- }
-
- nTs = (time_t)(uint64_t)val;
- }
- }
- while(false);
- return nTs;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::Flush(time_t nTimestamp)
- {
- ::pthread_mutex_lock(&m_condmtx1);
- bool bGoodBadTransition = false, bSgInProg = m_bSGInProgress;
- ::pthread_mutex_unlock(&m_condmtx1);
- time_t nMaxLogTimestamp = 0, nFirstGoodTimestamp = 0;
- size_t nCountValidDate = m_logs.size();
- size_t nCountBadDate = m_logsBD.size();
-
- if(!nCountValidDate && !nCountBadDate)
- return true; // nothing to do
-
- TRACE("Trying to flush %zu logs ...\n", nCountValidDate + nCountBadDate);
-
- if(bSgInProg && nCountValidDate)
- {
- TRACE("Flush: SG in progress - flushing of %zu logs deferred!\n", nCountValidDate);
- m_lf.Info("Flush: SG in progress - flushing of %zu logs deferred!\n", nCountValidDate);
- nCountValidDate = 0; // size guard operates on the valid-date-logs-table only, so we can safely flush invalid-date-logs
-
- if(!nCountBadDate)
- return true; // no error
- }
-
- if(!m_bBadDateLogsDetected && nCountBadDate)
- {
- m_bBadDateLogsDetected = true;
- bGoodBadTransition = true;
- }
- else if(m_bBadDateLogsDetected && !nCountBadDate)
- {
- m_bBadDateLogsDetected = false;
- bGoodBadTransition = true;
- }
- CMySqlDB db;
- std::string strUnlock, strSaveFgnKey, strDisableFgnKey, strRestoreFgnKey, strSaveTZ, strSetTZ, strRestoreTZ;
- bool bError = false;
- std::string sSql;
- if(!db.Connect("localhost", m_dlp.szDBUser, m_dlp.szDBPass, m_dlp.szDBName))
- {
- m_lf.Error("CDataLogger::Flush: DB Error: %s\n", db.LastError().c_str());
- return false;
- }
- strUnlock = formatString("unlock tables");
- strSaveFgnKey = formatString("set @old_foreign_key_checks = @@foreign_key_checks");
- strDisableFgnKey = formatString("set foreign_key_checks = 0");
- strRestoreFgnKey = formatString("set foreign_key_checks = @old_foreign_key_checks");
- strSaveTZ = formatString("set @old_time_zone = @@time_zone");
- strSetTZ = formatString("set time_zone = '+00:00'");
- strRestoreTZ = formatString("set time_zone = @old_time_zone");
- db.Query(strSaveTZ.c_str());
- db.Query(strSetTZ.c_str());
- db.Query(strSaveFgnKey.c_str());
- db.Query(strDisableFgnKey.c_str());
- if(nCountValidDate > 0)
- {
- std::string strSql, strLock;
- strLock = formatString("lock tables `%s` write", m_dlp.szLogsTable);
- strSql.reserve(130 + 111 * nCountValidDate);
- auto itFirst = m_logs.begin();
- const DL_LOG_ENTRY &rle0 = *itFirst;
- if(nMaxLogTimestamp < rle0.nTimestamp)
- nMaxLogTimestamp = rle0.nTimestamp;
- nFirstGoodTimestamp = rle0.nTimestamp;
- if(m_dlp.bMinMax && _IS_INTERVAL_LOGTYPE(rle0.lt))
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), %.20g, %.20g, %.20g)", m_dlp.szLogsTable, rle0.nTagID, rle0.nTimestamp, rle0.fValue, rle0.fMin, rle0.fMax);
- else
- {
- if(!rle0.bNull)
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), %.20g, NULL, NULL)", m_dlp.szLogsTable, rle0.nTagID, rle0.nTimestamp, rle0.fValue);
- else
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), NULL, NULL, NULL)", m_dlp.szLogsTable, rle0.nTagID, rle0.nTimestamp);
- }
- strSql = sSql;
- for(++itFirst; itFirst < m_logs.end(); itFirst++)
- {
- const DL_LOG_ENTRY &rle = *itFirst;
- if(nMaxLogTimestamp < rle.nTimestamp)
- nMaxLogTimestamp = rle.nTimestamp;
- if(m_dlp.bMinMax && _IS_INTERVAL_LOGTYPE(rle.lt))
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), %.20g, %.20g, %.20g)", rle.nTagID, rle.nTimestamp, rle.fValue, rle.fMin, rle.fMax);
- else
- {
- if(!rle.bNull)
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), %.20g, NULL, NULL)", rle.nTagID, rle.nTimestamp, rle.fValue);
- else
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), NULL, NULL, NULL)", rle.nTagID, rle.nTimestamp);
- }
- strSql += sSql;
- }
- if(TryLock())
- {
- db.Query(strLock.c_str());
- CMySqlResult res = db.Query(strSql.c_str());
- bError = res.error();
- db.Query(strUnlock.c_str());
- Unlock();
- if(bError)
- m_lf.Error("CDataLogger::Flush: DB Error: %s\n", db.LastError().c_str());
- else
- m_nLastLogTimestamp = nMaxLogTimestamp;
- m_logs.clear();
- }
- }
- if(bGoodBadTransition)
- {
- if(m_bBadDateLogsDetected)
- {
- bool bError;
- char szTs[64];
- unsigned long long nNextAIVal = 0;
- sSql = formatString("select auto_increment from `information_schema`.`TABLES` where `TABLE_SCHEMA` = '%s' and `TABLE_NAME` = '%s';", m_dlp.szDBName, m_dlp.szLogsTableBD);
- CMySqlResult res = db.Query(sSql.c_str());
- if(!(bError = res.error()))
- {
- CMySqlVar val;
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- do
- {
- if(nRowCount != 1 || nFldCount != 1 || !pFields)
- break;
- MYSQL_ROW pRow = res.FetchRow();
- if(!pRow || !*pRow || !**pRow)
- break;
- if(!val.FromField(pFields[0], pRow[0]))
- break;
- nNextAIVal = val;
- }
- while(false);
- }
-
- Timestamp2String(m_nLastLogTimestamp, szTs, sizeof(szTs));
- if(nNextAIVal)
- {
- TRACE("Flush: Transition valid -> invalid date detected! The last Timestamp in Table `%s` was '%s+00:00'. Subsequent logs will be written to table `%s` starting with `id` %llu.\n", m_dlp.szLogsTable, szTs, m_dlp.szLogsTableBD, nNextAIVal);
- m_lf.Warning("Flush: Transition valid -> invalid date detected! The last Timestamp in Table `%s` was '%s+00:00'. Subsequent logs will be written to table `%s` starting with `id` %llu.\n", m_dlp.szLogsTable, szTs, m_dlp.szLogsTableBD, nNextAIVal);
- }
- else
- {
- TRACE("Flush: Transition valid -> invalid date detected! The last Timestamp in Table `%s` was '%s+00:00'. Subsequent logs will be written to table `%s`.\n", m_dlp.szLogsTable, szTs, m_dlp.szLogsTableBD);
- m_lf.Warning("Flush: Transition valid -> invalid date detected! The last Timestamp in Table `%s` was '%s+00:00'. Subsequent logs will be written to table `%s`.\n", m_dlp.szLogsTable, szTs, m_dlp.szLogsTableBD);
- }
- }
- else
- {
- bool bError;
- char szTs[64];
- unsigned long long nLastID = 0;
- sSql = formatString("select max(`id`) from `%s`.`%s`;", m_dlp.szDBName, m_dlp.szLogsTableBD);
- CMySqlResult res = db.Query(sSql.c_str());
- if(!(bError = res.error()))
- {
- CMySqlVar val;
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- do
- {
- if(nRowCount != 1 || nFldCount != 1 || !pFields)
- break;
- MYSQL_ROW pRow = res.FetchRow();
- if(!pRow || !*pRow || !**pRow)
- break;
- if(!val.FromField(pFields[0], pRow[0]))
- break;
- nLastID = val;
- }
- while(false);
- }
-
- Timestamp2String(nFirstGoodTimestamp, szTs, sizeof(szTs));
- if(nLastID)
- {
- TRACE("Flush: Transition invalid -> valid date detected! The last `id` in table `%s` was %llu. Subsequent logs will be written to table `%s` beginning with timestamp '%s+00:00'.\n", m_dlp.szLogsTableBD, nLastID, m_dlp.szLogsTable, szTs);
- m_lf.Warning("Flush: Transition invalid -> valid date detected! The last `id` in table `%s` was %llu. Subsequent logs will be written to table `%s` beginning with timestamp '%s+00:00'.\n", m_dlp.szLogsTableBD, nLastID, m_dlp.szLogsTable, szTs);
- }
- else
- {
- TRACE("Flush: Transition invalid -> valid date detected! Subsequent logs will be written to table `%s` beginning with timestamp '%s+00:00'.\n", m_dlp.szLogsTable, szTs);
- m_lf.Warning("Flush: Transition invalid -> valid date detected! Subsequent logs will be written to table `%s` beginning with timestamp '%s+00:00'.\n", m_dlp.szLogsTable, szTs);
- }
- }
- }
- if(nCountBadDate > 0)
- {
- std::string strSql, strLock;
- strLock = formatString("lock tables `%s` write", m_dlp.szLogsTableBD);
- strSql.reserve(130 + 111 * nCountBadDate);
- auto itFirst = m_logsBD.begin();
- const DL_LOG_ENTRY &rle0 = *itFirst;
- if(m_dlp.bMinMax && _IS_INTERVAL_LOGTYPE(rle0.lt))
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), %.20g, %.20g, %.20g)", m_dlp.szLogsTableBD, rle0.nTagID, rle0.nTimestamp, rle0.fValue, rle0.fMin, rle0.fMax);
- else
- {
- if(!rle0.bNull)
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), %.20g, NULL, NULL)", m_dlp.szLogsTableBD, rle0.nTagID, rle0.nTimestamp, rle0.fValue);
- else
- sSql = formatString("insert into `%s` (`tagid`, `tslog`, `value`, `valueMin`, `valueMax`) values (%lu, timestamp(from_unixtime(%lu)), NULL, NULL, NULL)", m_dlp.szLogsTableBD, rle0.nTagID, rle0.nTimestamp);
- }
- strSql = sSql;
- for(++itFirst; itFirst < m_logsBD.end(); itFirst++)
- {
- const DL_LOG_ENTRY &rle = *itFirst;
- if(m_dlp.bMinMax && _IS_INTERVAL_LOGTYPE(rle.lt))
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), %.20g, %.20g, %.20g)", rle.nTagID, rle.nTimestamp, rle.fValue, rle.fMin, rle.fMax);
- else
- {
- if(!rle.bNull)
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), %.20g, NULL, NULL)", rle.nTagID, rle.nTimestamp, rle.fValue);
- else
- sSql = formatString(",(%lu, timestamp(from_unixtime(%lu)), NULL, NULL, NULL)", rle.nTagID, rle.nTimestamp);
- }
- strSql += sSql;
- }
- db.Query(strLock.c_str());
- CMySqlResult res = db.Query(strSql.c_str());
- bError = res.error();
- db.Query(strUnlock.c_str());
- if(bError)
- m_lf.Error("CDataLogger::Flush: DB Error: %s\n", db.LastError().c_str());
- m_logsBD.clear();
- }
- db.Query(strRestoreFgnKey.c_str());
- db.Query(strRestoreTZ.c_str());
- return !bError;
- }
- /////////////////////////////////////////////////////////////////////////////
- unsigned long long CDataLogger::SizeGuardLastPassRead(void)
- {
- char szPath[PATH_MAX];
- sprintf(szPath, "%s/%s", m_szAppDir, _SIZE_GUARD_TIMESTAMP_FILE_NAME);
- FILE *pf = fopen(szPath, "rb");
- unsigned long long ts = 0;
- if(pf)
- {
- if(fread(&ts, sizeof(ts), 1, pf) != 1)
- ts = 0;
- fclose(pf);
- }
- return ts;
- }
- /////////////////////////////////////////////////////////////////////////////
- void CDataLogger::SizeGuardLastPassWrite(unsigned long long ts)
- {
- char szPath[PATH_MAX];
- sprintf(szPath, "%s/%s", m_szAppDir, _SIZE_GUARD_TIMESTAMP_FILE_NAME);
- FILE *pf = fopen(szPath, "wb");
- if(pf)
- {
- fwrite(&ts, sizeof(ts), 1, pf);
- fclose(pf);
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- size_t CDataLogger::Timestamp2String(time_t t, char *pszBuffer, size_t nCbBuffer)
- {
- const struct tm * ptm = gmtime(&t);
- return strftime(pszBuffer, nCbBuffer, "%F %T", ptm);
- }
- /////////////////////////////////////////////////////////////////////////////
- const char* CDataLogger::Ns2String(unsigned long long nNs, char *pszBuffer, size_t nCbBuffer)
- {
- *pszBuffer = '\0';
- if(nNs < 1000)
- snprintf(pszBuffer, nCbBuffer, "%llu ns", nNs);
- else if(nNs < 1000000)
- snprintf(pszBuffer, nCbBuffer, "%.1f us", (double)nNs / 1000.0);
- else
- return Ms2String((double)nNs / 1000000.0, pszBuffer, nCbBuffer);
- return pszBuffer;
- }
- /////////////////////////////////////////////////////////////////////////////
- const char* CDataLogger::Ms2String(double fMs, char *pszBuffer, size_t nCbBuffer)
- {
- *pszBuffer = '\0';
- if(fMs < 1000.0)
- snprintf(pszBuffer, nCbBuffer, "%.2f ms", fMs);
- else if(fMs < 60000.0)
- snprintf(pszBuffer, nCbBuffer, "%.2f sec", fMs / 1000.0);
- else if(fMs < 3600000.0)
- snprintf(pszBuffer, nCbBuffer, "%.2f min", fMs / 60000.0);
- else
- snprintf(pszBuffer, nCbBuffer, "%.2f h", fMs / 3600000.0);
-
- return pszBuffer;
- }
- /////////////////////////////////////////////////////////////////////////////
- bool CDataLogger::DoSizeGuard(void)
- {
- if(!m_bSGConfigured)
- return true;
- CMySqlDB db;
- bool bError;
- MYSQL_ROW pRow;
- unsigned long long nElapsed;
- char szT1[32], szT2[32], szMs[32];
- CProcessClock pc;
- CMySqlVar vMinTs, vCountDelete;
- int64_t nMinTs, nTsCurMidnightUTC, nTsDeleteUpTo;
- std::string sSql;
- /////////////////////////////////////////////////////////////////////////
- nTsCurMidnightUTC = _MIDNIGHT_TIMESTAMP_UTC(m_nSGCurPassUTC);
- if(SizeGuardDayWorkDone(nTsCurMidnightUTC))
- return true;
- m_nSGLastPassUTC = nTsCurMidnightUTC;
- if(SizeGuardLastPassRead() == (unsigned long long)(nTsCurMidnightUTC))
- return true;
- SizeGuardLastPassWrite(nTsCurMidnightUTC);
- /////////////////////////////////////////////////////////////////////////
- if(!db.Connect("localhost", m_dlp.szDBUser, m_dlp.szDBPass, NULL))
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): - DB Error: %s\n", __LINE__, db.LastError().c_str());
- return false;
- }
- sSql = formatString("select unix_timestamp(min(`tslog`)) from `%s`.`%s`;", m_dlp.szDBName, m_dlp.szLogsTable);
- m_lf.Info("SG: \"%s\".\n", sSql.c_str());
- pc.ClockTrigger();
- CMySqlResult res = db.Query(sSql.c_str());
- nElapsed = pc.ClockGetElapsed();
-
- bError = res.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): \"%s\" - DB Error: %s\n", __LINE__, sSql.c_str(), db.LastError().c_str());
- return false;
- }
- if(!(pRow = res.FetchRow()))
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): Unexpected Error!\n", __LINE__);
- return false;
- }
- my_ulonglong nRowCount = res.RowCount();
- unsigned int nFldCount = res.FieldCount();
- const MYSQL_FIELD *pFields = res.FetchFields();
- if(nRowCount != 1 || nFldCount != 1)
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): Unexpected Error!\n", __LINE__);
- return false;
- }
- if(!vMinTs.FromField(pFields[0], pRow[0]))
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): Unexpected Error!\n", __LINE__);
- return false;
- }
- nMinTs = vMinTs;
- m_lf.Info("SG: operation completed in %s. Min. timestamp: %ju\n", Ns2String(nElapsed, szMs, sizeof(szMs)), nMinTs);
- /////////////////////////////////////////////////////////////////////////
- // process max. size
- if(m_bSGHasSizeLimitPrerequisites && m_dlp.nMaxSize)
- {
- unsigned long long nFileSize = TableFileSize(m_dlp.szLogsTable);
- unsigned long long nGuardThreshold = m_dlp.nMaxSize * (100 - _FILE_SIZE_DELETE_MARGIN_PERCENT) / 100;
- m_lf.Info("SG: File size: %s.\n", strFormatByteSize(nFileSize, 1).c_str());
- if(nFileSize > nGuardThreshold)
- {
- Timestamp2String(nTsCurMidnightUTC - _SECONDS_PER_DAY, szT1, sizeof(szT1));
- Timestamp2String(nTsCurMidnightUTC - 1, szT2, sizeof(szT2));
- sSql = formatString("select count(*) from `%s`.`%s` where `tslog` between '%s' and '%s';", m_dlp.szDBName, m_dlp.szLogsTable, szT1, szT2);
- m_lf.Info("SG: \"%s\"\n", sSql.c_str());
- pc.ClockTrigger();
- CMySqlResult res = db.Query(sSql.c_str());
- bError = res.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): \"%s\" - DB Error: %s\n", __LINE__, sSql.c_str(), db.LastError().c_str());
- return false;
- }
- else
- {
- nElapsed = pc.ClockGetElapsed();
- nRowCount = res.RowCount();
- nFldCount = res.FieldCount();
- pFields = res.FetchFields();
- pRow = res.FetchRow();
- if(pRow && pFields && nRowCount == 1 && nFldCount == 1 && vCountDelete.FromField(pFields[0], pRow[0]))
- {
- unsigned long long nCountDelete = (uint64_t)vCountDelete;
- m_lf.Info("SG: operation completed in %s. Estimated number of rows to delete: %llu.\n", Ns2String(nElapsed, szMs, sizeof(szMs)), nCountDelete);
- if(nCountDelete > 0)
- {
- #if _USE_MODIFIED_INDEX
- sSql = formatString("delete from `%s`.`%s` limit %llu;", m_dlp.szDBName, m_dlp.szLogsTable, nCountDelete);
- #else // _USE_MODIFIED_INDEX
- sSql = formatString("delete from `%s`.`%s` where `tslog` <= (select `tslog` from (select `tslog` from `demo`.`logs` order by `tslog` asc limit %llu, 1) x);", m_dlp.szDBName, m_dlp.szLogsTable, nCountDelete - 1);
- #endif // _USE_MODIFIED_INDEX
- m_lf.Info("SG: triggered on size limit: \"%s\".\n", sSql.c_str());
- pc.ClockTrigger();
- if(db.Query(sSql.c_str()).error())
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): \"%s\" - DB Error: %s\n", __LINE__, sSql.c_str(), db.LastError().c_str());
- return false;
- }
- else
- {
- nElapsed = pc.ClockGetElapsed();
- m_lf.Info("SG: operation completed in %s.\n", Ns2String(nElapsed, szMs, sizeof(szMs)));
- }
- }
- }
- else
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): Unexpected Error!\n", __LINE__);
- return false;
- }
- }
- }
- }
- /////////////////////////////////////////////////////////////////////////
- // process max. age entries
- if(m_dlp.nMaxAge)
- {
- nTsDeleteUpTo = nTsCurMidnightUTC - m_dlp.nMaxAge * _SECONDS_PER_DAY;
- if(nMinTs <= nTsDeleteUpTo)
- {
- Timestamp2String(nTsDeleteUpTo, szT1, sizeof(szT1));
- sSql = formatString("delete from `%s`.`%s` where `tslog` < '%s';", m_dlp.szDBName, m_dlp.szLogsTable, szT1);
- m_lf.Info("SG: triggered on age limit: \"%s\".\n", sSql.c_str());
- pc.ClockTrigger();
- CMySqlResult res = db.Query(sSql.c_str());
- nElapsed = pc.ClockGetElapsed();
- bError = res.error();
- if(bError)
- {
- m_lf.Error("CDataLogger::DoSizeGuard(%d): DB Error: %s\n", __LINE__, db.LastError().c_str());
- return false;
- }
- else
- {
- m_lf.Info("SG: operation completed in %s.\n", Ns2String(nElapsed, szMs, sizeof(szMs)));
- }
- }
- }
- return true;
- }
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- // size guard worker thread
- void* CDataLogger::SizeGuardWorker(void* pParam)
- {
- unsigned long long nElapsed;
- char szMs[32];
- CProcessClock pc;
- CDataLogger *pThis = reinterpret_cast<CDataLogger*>(pParam);
- ::pthread_mutex_lock(&pThis->m_condmtx1);
- ::pthread_cond_signal(&pThis->m_cond1);
- while(true)
- {
- pThis->m_bSGInProgress = false;
- ::pthread_cond_wait(&pThis->m_cond1, &pThis->m_condmtx1);
- pThis->m_bSGInProgress = true;
- ::pthread_mutex_unlock(&pThis->m_condmtx1);
- TRACE("Size guard start.\n");
- pThis->Lock();
- pc.ClockTrigger();
- pThis->DoSizeGuard();
- nElapsed = pc.ClockGetElapsed();
- pThis->Unlock();
- pThis->m_lf.Info("SG: finished in %s.\n", Ns2String(nElapsed, szMs, sizeof(szMs)));
- TRACE("Size guard end - [%s].\n", szMs);
- ::pthread_yield();
- ::pthread_mutex_lock(&pThis->m_condmtx1);
- }
- return NULL;
- }
|