khanat-opennel-code/code/ryzom/server/src/pd_lib/pd_lib.cpp

1160 lines
27 KiB
C++
Raw Normal View History

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "pd_lib.h"
#include <nel/misc/file.h>
#include <nel/misc/o_xml.h>
#include <nel/net/service.h>
#include <nel/net/unified_network.h>
#include <nel/net/service.h>
#include "pd_string_mapper.h"
#include "timestamp.h"
#include "db_description_parser.h"
#include "game_share/backup_service_interface.h"
using namespace std;
using namespace NLMISC;
using namespace NLNET;
namespace RY_PDS
{
std::vector<CPDSLib*> CPDSLib::_Libs;
/*
* PD System Verbosity control
*/
CVariable<bool> PDVerbose("pd", "PDVerbose", "Control of the PD system verbosity", false, 0, true);
/**
* PD System Verbosity level
*/
CVariable<sint> PDVerboseLevel("pd", "PDVerboseLevel", "PD system verbosity level (the greater the more the service logs info, -1=no log, default=0)", 0, 0, true);
/*
* PDS Resolve Unmapped rows
* If true, PDS continue loading when it finds a row not mapped in a mapped table,
* and keeps it unmapped.
* Otherwise, loading fails.
*/
CVariable<bool> ResolveUnmappedRows("pd", "ResolveUnmappedRows", "If true, PDS continue loading when it finds a row not mapped in a mapped table, and keeps it unmapped. Otherwise, loading fails.", true, 0, true);
/*
* PDS Resolve Double Mapped Rows
* If true, PDS continue loading when it finds a row already mapped, and keeps it unmapped.
* Otherwise, loading fails.
* See also ResolveDoubleMappedKeepOlder
*/
CVariable<bool> ResolveDoubleMappedRows("pd", "ResolveDoubleMappedRows", "If true, PDS continue loading when it finds a row already mapped, and keeps it unmapped. Otherwise, loading fails. See also ResolveDoubleMappedKeepOlder", true, 0, true);
/*
* PDS Resolve Double Mapped Keep Older
* If true, when finds a doubly mapped row at loading, keep only older row as mapped (lesser row index)
* See also ResolveDoubleMappedRows.
*/
CVariable<bool> ResolveDoubleMappedKeepOlder("pd", "ResolveDoubleMappedKeepOlder", "If true, when finds a doubly mapped row at loading, keep only older row as mapped (lesser row index). See also ResolveDoubleMappedRows.", true, 0, true);
/*
* PDS Resolve Unallocated rows
* If true, when finds a reference to an unallocated row or an invalid row, force reference to null.
*/
CVariable<bool> ResolveInvalidRow("pd", "ResolveInvalidRow", "If true, when finds a reference to an unallocated row or an invalid row, force reference to null.", true, 0, true);
/*
* Maximal Message Queue Size in bytes
* Set to 1mb by default
*/
CVariable<uint> PDMaxMsgQueueSize("pd", "PDMaxMsgQueueSize", "Maximum message queue size towards PDS. If queue size is greater, PDS is displayed as not available, and processing should be halted", 1048576, 0, true);
/*
* Enables client to log updates by itself (when PDS is not used)
*/
CVariable<bool> PDEnableLog("pd", "PDEnableLog", "Enables client to log updates by itself (when PDS is not used)", true, 0, true);
/*
* Enables client to log string manager updates by itself (when PDS is not used)
*/
//CVariable<bool> PDEnableStringLog("pd", "PDEnableStringLog", "Enables client to log string manager updates by itself (when PDS is not used)", true, 0, true);
/*
* Number of seconds between 2 updates (when PDS is not used)
* Set to 1mb by default
*/
CVariable<uint> PDLogUpdate("pd", "PDLogUpdate", "Number of seconds between 2 updates (when PDS is not used)", 5, 0, true);
/*
* Time between creating 2 different pd_log file (in seconds)
*/
CVariable<uint> PDLogFileTime("pd", "PDLogFileTime", "Time between creating 2 different pd_log file (in seconds)", 3600, 0, true);
/*
* Directory to save logs (when PDS is not used)
*/
CVariable<std::string> PDRootDirectory("pd", "PDRootDirectory", "Directory to save PD data (if empty, 'SaveFilesDirectory/pds' will be used instead)", "", 0, true);
/*
* Use Backup Servce when logging
*/
CVariable<bool> PDUseBS("pd", "PDUseBS", "Use Backup Service when logging (when PDS is not used)", true, 0, true);
/*
* PDS is ready to work
*/
void cbPDSReady(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
uint32 lastUpdateId;
msgin.serial(lastUpdateId);
CPDSLib* lib = CPDSLib::getLib(databaseId);
if (lib != NULL)
lib->ready(lastUpdateId);
}
/*
* PDS init failed, stop everything!
*/
void cbPDSInitFailed(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
string error;
msgin.serial(error);
nlerror("Database '%d' initialisation failed: '%s'", databaseId, error.c_str());
}
/*
* fetch data
*/
void cbFetch(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
CPDSLib* lib = CPDSLib::getLib(databaseId);
if (lib != NULL)
lib->fetchPDSData(msgin);
}
/*
* fetch data failed
*/
void cbFetchFailure(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
CPDSLib* lib = CPDSLib::getLib(databaseId);
if (lib != NULL)
lib->notifyFetchFailure(msgin);
}
/*
* receive index allocators
*/
void cbAllocs(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
CPDSLib* lib = CPDSLib::getLib(databaseId);
if (lib == NULL)
return;
// do not reinit allocator as it may contain newer data
if (lib->allocsInitialised())
return;
lib->setupIndexAllocators(msgin);
lib->initAllocs();
}
/*
* init client string manager
*/
//void cbInitStringManager(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
//{
// uint32 databaseId;
// msgin.serial(databaseId);
//
// CPDSLib* lib = CPDSLib::getLib(databaseId);
//
// if (lib == NULL)
// return;
//
// // do not reinit string manager (as it may contain newer data)
// if (lib->stringManagerInitialised())
// return;
//
// CPDStringManager& sm = lib->getStringManager();
// sm.serial(msgin);
//
// lib->initStringManager();
//}
/*
* Flush client messages, when they are acknowledged by the PDS
*/
void cbFlushAckUpdates(NLNET::CMessage& msgin, const std::string &serviceName, TServiceId serviceId)
{
uint32 databaseId;
msgin.serial(databaseId);
vector<uint32> ack;
msgin.serialCont(ack);
CPDSLib* lib = CPDSLib::getLib(databaseId);
if (lib != NULL)
lib->flushAcknowledged(ack);
}
TUnifiedCallbackItem PDSCbArray[] =
{
{ "PD_READY", cbPDSReady },
{ "PD_INIT_FAILED", cbPDSInitFailed },
{ "PD_FETCH", cbFetch },
{ "PD_FETCH_FAILURE", cbFetchFailure },
{ "PD_ALLOCS", cbAllocs },
// { "PD_SM_INIT", cbInitStringManager },
{ "PD_ACK_UPD", cbFlushAckUpdates },
};
/*
* Constructor
*/
CPDSLib::CPDSLib()
{
_PDSConnected = false;
_PDSReady = false;
_DatabaseId = 0-1;
_UpdateId = 0-1;
_UsePDS = false;
_AllocsInitialised = false;
// _StringManagerInitialised = false;
_PreviousLogSave = 0;
}
/*
* Internal type checking
*/
void CPDSLib::checkInternalTypes()
{
if (sizeof(CColumnIndex) != 8)
nlerror("Internal type checking failed: class CColumnIndex is not 8 bytes wide");
if (sizeof(CObjectIndex) != 8)
nlerror("Internal type checking failed: class CObjectIndex is not 8 bytes wide");
}
void CPDSLib::usePDS()
{
_UsePDS = true;
}
// Init Db description
void CPDSLib::init(const std::string &xml, uint32 overrideDbId)
{
nlinfo("CPDSLib: %12s", "init");
// initial checkup
checkInternalTypes();
_XmlDescription = xml;
CUnifiedNetwork::getInstance()->addCallbackArray(PDSCbArray, sizeof(PDSCbArray)/sizeof(TUnifiedCallbackItem));
CUnifiedNetwork::getInstance()->setServiceUpCallback("PDS", onPDSUp);
CUnifiedNetwork::getInstance()->setServiceDownCallback("PDS", onPDSDown);
CConfigFile::CVar* dbidptr = IService::getInstance()->ConfigFile.getVarPtr("DatabaseId");
if (overrideDbId != 0)
{
nlinfo("CPDSLib: variable 'DatabaseId' orverriden with Id='%d'", overrideDbId);
_DatabaseId = overrideDbId;
}
else if (dbidptr != NULL && overrideDbId == 0)
{
_DatabaseId = dbidptr->asInt();
}
else
{
nlwarning("CPDSLib: variable 'DatabaseId' not found in config file, will use '0' as default value");
_DatabaseId = 0;
}
if (_Libs.size() <= _DatabaseId)
_Libs.resize(_DatabaseId+1, NULL);
_Libs[_DatabaseId] = this;
// _StringManager.init(this);
if (!_UsePDS)
{
// _StringManager.load();
// save description in log repertory
std::string logDir = getRemoteLogDirectory();
bool repSuccess = true;
if (!CFile::isDirectory(logDir))
{
if (!CFile::createDirectoryTree(logDir))
{
nlwarning("Failed to create log root directory '%s'", logDir.c_str());
repSuccess = false;
}
if (!CFile::setRWAccess(logDir))
{
nlwarning("Failed, can't set RW access to directory '%s'", logDir.c_str());
repSuccess = false;
}
}
_LogStartDate.setToCurrent();
_PreviousTickDate = _LogStartDate;
if (repSuccess)
{
CDBDescriptionParser dbDesc;
dbDesc.loadDescription((const uint8*)(xml.c_str()));
// dbDesc.saveDescription(logDir + _LogStartDate.toString() + ".description");
}
}
}
/*
* Init PDS log
*/
void CPDSLib::initLog(uint logmsg)
{
if (logmsg >= _LogDescs.size())
_LogDescs.resize(logmsg + 1);
nlassertex(_LogDescs[logmsg].Log == 0xffffffff, ("Internal error! Log message %d already declared!", logmsg));
_LogDescs[logmsg].Log = logmsg;
_LogDescs[logmsg].ByteSize = 0;
_LogDescs[logmsg].Params.clear();
}
/*
* Init PDS log parameter
*/
void CPDSLib::initLogParam(uint logmsg, uint logparam, uint byteSize)
{
nlassertex(_LogDescs.size() > logmsg, ("Internal error! Log message %d is not yet declared!", logmsg));
CLogDesc& desc = _LogDescs[logmsg];
nlassertex(desc.Log == logmsg, ("Internal error! Log message %d badly or not initialised!", logmsg));
nlassertex(desc.Params.size() == logparam, ("Internal error! Log parameter %d of message %d already initialised!", logmsg, logparam));
uint byteOffset = desc.ByteSize;
CLogParam param;
param.ByteSize = byteSize;
param.ByteOffset = byteOffset;
desc.Params.push_back(param);
desc.ByteSize += byteSize;
}
/*
* Release Lib
*/
void CPDSLib::release()
{
}
/*
* PDS up callback
*/
void CPDSLib::onPDSUp(const std::string &serviceName, TServiceId sid, void *arg)
{
uint i;
for (i=0; i<_Libs.size(); ++i)
{
if (_Libs[i] != NULL)
{
_Libs[i]->_PDSConnected = true;
_Libs[i]->_PDSReady = false;
_Libs[i]->connect();
}
}
}
/*
* Connect service to PDS when up
*/
void CPDSLib::connect()
{
nlassert(_PDSConnected);
nlinfo("PDS is up, connecting...");
CMessage msgout("PD_INIT");
msgout.serial(_DatabaseId);
msgout.serial(_XmlDescription);
CUnifiedNetwork::getInstance()->send("PDS", msgout);
}
/*
* PDS is ready
*/
void CPDSLib::ready(uint32 lastUpdateId)
{
nlassert(_PDSConnected);
nlinfo("PDS is ready to work");
_PDSReady = true;
// set up next update Id
_UpdateId = lastUpdateId+1;
// flush previous messages that have already been written on hd
if ((sint)lastUpdateId >= 0)
{
flushAcknowledged(lastUpdateId);
}
// prepare PD string mapper for sheet ids
CPDStringMapper sheetIdStringMapper;
std::vector<NLMISC::CSheetId> sheetIds;
NLMISC::CSheetId::buildIdVector(sheetIds);
uint i;
for (i=0; i<sheetIds.size(); ++i)
{
NLMISC::CSheetId sheetId = sheetIds[i];
sheetIdStringMapper.setMapping(sheetId.toString(), sheetId.asInt());
}
CMessage msgout("PD_SHEETID_MAPPING");
msgout.serial(_DatabaseId);
msgout.serial(sheetIdStringMapper);
CUnifiedNetwork::getInstance()->send("PDS", msgout);
// if there are still some messages in queue, resend them
if (!_QueuedMessages.empty())
{
// sort them before, so they are in right order
// this is in theory useless, since new messages are inserted at end
std::sort(_QueuedMessages.begin(), _QueuedMessages.end(), CQueuedMessagePred());
TQueuedMessages::iterator it;
for (it=_QueuedMessages.begin(); it!=_QueuedMessages.end(); ++it)
{
uint32 updateId = (*it).first;
if (updateId != _UpdateId)
nlwarning("CPDSLib::ready(): update id '%d' is not consistent with message id '%d' to be sent", _UpdateId, updateId);
_UpdateId = updateId+1;
NLNET::CMessage* msg = (*it).second;
CUnifiedNetwork::getInstance()->send("PDS", *msg);
}
}
}
/*
* PDS down callback
*/
void CPDSLib::onPDSDown(const std::string &serviceName, TServiceId sid, void *arg)
{
uint i;
for (i=0; i<_Libs.size(); ++i)
{
if (_Libs[i] != NULL)
{
_Libs[i]->_PDSConnected = false;
_Libs[i]->_PDSReady = false;
_Libs[i]->disconnect();
}
}
}
/*
* Disconnect service to PDS
*/
void CPDSLib::disconnect()
{
nlinfo("PDS disconnected, system should be halted");
}
/*
* Tells if PDS is ready
*/
bool CPDSLib::PDSReady()
{
// check PDS connected, ready and is not too busy
return (!_UsePDS) || (_PDSReady && _PDSConnected && getMessageQueueSize() < PDMaxMsgQueueSize);
}
/*
* Update
* Call regularly this method once a tick to apply changes to database
*/
void CPDSLib::update()
{
if (_UsePDS)
{
// check pds is ready to receive update
if (!PDSReady())
return;
if (_DbMessageQueue.empty())
return;
std::list<CDbMessageQueue>::iterator it;
for (it=_DbMessageQueue.begin(); it!=_DbMessageQueue.end(); ++it)
{
CDbMessageQueue& queue = (*it);
// create a new message
CMessage* msgupd = new CMessage("PD_UPDATE");
msgupd->serial(_DatabaseId);
msgupd->serial(_UpdateId);
_QueuedMessages.push_back(make_pair<uint32,CMessage*>(_UpdateId, msgupd));
++_UpdateId;
// serial queue
msgupd->serial(queue);
// send update to the pds
CUnifiedNetwork::getInstance()->send("PDS", *msgupd);
}
// clean up for next update
_DbMessageQueue.clear();
}
else
{
uint i;
// save log (updates + string manager)
TTime ctime = CTime::getLocalTime();
//
CTimestamp timestamp;
timestamp.setToCurrent();
// setup update logs with full timestamp
if (!_DbMessageQueue.empty())
{
i = (uint)_UpdateLogs.size();
_UpdateLogs.resize(_DbMessageQueue.size());
for (; i<_UpdateLogs.size(); ++i)
{
_UpdateLogs[i].StartStamp = _PreviousTickDate;
_UpdateLogs[i].EndStamp = _PreviousTickDate;
}
_UpdateLogs.back().EndStamp = timestamp;
}
_PreviousTickDate = timestamp;
if (ctime - _PreviousLogSave > PDLogUpdate*1000 && !_DbMessageQueue.empty())
{
// std::string logDir = getRemoteLogDirectory();
// std::string logFile = _LogStartDate.toString()+"_0000.pd_log";
//
// // if don't use BS, create logdir directory tree
// if (!PDUseBS && !CFile::isDirectory(logDir))
// {
// if (!CFile::createDirectoryTree(logDir))
// {
// nlwarning("Failed to create log root directory '%s'", logDir.c_str());
// }
//
// if (!CFile::setRWAccess(logDir))
// {
// nlwarning("Failed, can't set RW access to directory '%s'", logDir.c_str());
// }
// }
//
// // map update logs to msg queues
// nlassert(_UpdateLogs.size() == _DbMessageQueue.size());
// for (i=0; i<_UpdateLogs.size(); ++i)
// _UpdateLogs[i].setUpdates(&(_DbMessageQueue.get(i)));
//
// if (PDUseBS)
// {
// CBackupMsgSaveFile msgBS( logDir+logFile, CBackupMsgSaveFile::AppendFileCheck, PDBsi );
// msgBS.DataMsg.serialCont(_UpdateLogs);
// PDBsi.append(msgBS);
// }
// else
// {
// COFile file;
// if (file.open(logDir + logFile))
// {
// file.serialCont(_UpdateLogs);
// }
// else
// {
// nlwarning("Failed to save log file '%s' for database %d", (logDir + logFile).c_str(), _DatabaseId);
// }
// }
// if (PDEnableStringLog && !_StringManager.logEmpty())
// {
// std::string logFile = timestamp.toString()+".string_log";
//
// if (PDUseBS)
// {
// bool success = false;
// CBackupMsgSaveFile msgBS( logDir+logFile, CBackupMsgSaveFile::SaveFileCheck, PDBsi );
// {
// COXml xml;
//
// if (xml.init(&msgBS.DataMsg))
// {
// _StringManager.storeLog(xml);
// success = true;
// }
// else
// {
// nlwarning("Failed to save string log file '%s' for database %d", msgBS.FileName.c_str(), _DatabaseId);
// }
// }
//
// if (success)
// {
// PDBsi.sendFile(msgBS);
// }
// }
// else
// {
// COFile file;
// {
// COXml xml;
// if (file.open(logDir + logFile) && xml.init(&file))
// {
// _StringManager.storeLog(xml);
// }
// else
// {
// nlwarning("Failed to save string log file '%s' for database %d", (logDir+logFile).c_str(), _DatabaseId);
// }
// }
// }
// }
_PreviousLogSave = ctime;
// clean up for next update
_DbMessageQueue.clear();
_UpdateLogs.clear();
}
if (timestamp.toTime()-_LogStartDate.toTime() > sint(PDLogFileTime.get()))
{
_LogStartDate = timestamp;
}
_DbMessageQueue.forceNextQueue();
}
}
/*
* Register Class Mapping
*/
void CPDSLib::registerClassMapping(TTableIndex table, const std::string& name)
{
nlassertex(_ClassMapName.find(name) == _ClassMapName.end(), ("Class Name '%s' already registered!", name.c_str()));
nlassertex(table >= _ClassMapId.size() || _ClassMapId[table].empty(), ("Class Id '%d' already registered!", table));
if (_ClassMapId.size() <= table)
_ClassMapId.resize(table+1);
_ClassMapName[name] = table;
_ClassMapId[table] = name;
}
/*
* Map Class Name
*/
TTableIndex CPDSLib::mapClassName(const std::string& name)
{
std::map<std::string, TTableIndex>::iterator it = _ClassMapName.find(name);
if (it == _ClassMapName.end())
return INVALID_TABLE_INDEX;
return (*it).second;
}
/*
* Map Class Name
*/
std::string CPDSLib::mapClassId(TTableIndex table)
{
if (_ClassMapId.size() <= table)
return "";
return _ClassMapId[table];
}
// Allocate a row in the PDS (key is provided for table that are keymapped
void CPDSLib::allocateRow(TTableIndex table, TRowIndex row, uint64 key)
{
// send to PDS alloc(table, row, key);
if (PDVerbose)
nlinfo("CPDSLib: %12s index=%u:%u, key=%016"NL_I64"X", "allocrow", table, row, key);
CDbMessage& msg = nextMessage((uint8)table, row);
msg.allocRow(key);
}
// Deallocate a row in the PDS
void CPDSLib::deallocateRow(TTableIndex table, TRowIndex row)
{
// send to PDS dealloc(table, row)
if (PDVerbose)
nlinfo("CPDSLib: %12s index=%u:%u", "deallocrow", table, row);
CDbMessage& msg = nextMessage((uint8)table, row);
msg.deallocRow();
}
// Allocate a row in the PDS (key is provided for table that are keymapped
void CPDSLib::allocateRow(TTableIndex table, TRowIndex row, uint64 key, const NLMISC::CEntityId& id)
{
// send to PDS alloc(table, row, key);
if (PDVerbose)
nlinfo("CPDSLib: %12s index=%u:%u, key=%016"NL_I64"X", "allocrow", table, row, key);
CDbMessage& msg = nextMessage((uint8)table, row);
msg.allocRow(key, id);
}
// Deallocate a row in the PDS
void CPDSLib::deallocateRow(TTableIndex table, TRowIndex row, const NLMISC::CEntityId& id)
{
// send to PDS dealloc(table, row)
if (PDVerbose)
nlinfo("CPDSLib: %12s index=%u:%u", "deallocrow", table, row);
CDbMessage& msg = nextMessage((uint8)table, row);
msg.deallocRow(id);
}
// Create an object of a given type
IPDBaseData *CPDSLib::create(TTableIndex table)
{
if (PDVerbose)
nlinfo("CPDSLib: %12s table=%u", "create", table);
if (table >= _Factories.size() || _Factories[table] == NULL)
{
nlwarning("CPDSLib: Unable to create object %d, factory not registered.", table);
return NULL;
}
IPDBaseData *obj = _Factories[table] ();
return obj;
}
// Fetch data
void CPDSLib::fetchPDSData(NLMISC::IStream &f)
{
TTableIndex table;
TRowIndex row;
f.serial(table, row);
IPDBaseData* obj = create(table);
if (obj == NULL)
{
nlerror("PDS sent an invalid object reference!");
}
setRowIndex(row, obj);
if (table >= _Fetchs.size() || _Fetchs[table] == NULL)
{
nlerror("PDS sent an invalid object reference! -- cannot fetch object data");
}
TPDFetch fetchFunc = _Fetchs[table];
fetchFunc(obj, f);
}
// Notify client of fetch failure
void CPDSLib::notifyFetchFailure(NLMISC::IStream &f)
{
TTableIndex table;
uint64 key;
f.serial(table, key);
if (table >= _FetchFailures.size() || _FetchFailures[table] == NULL)
{
nlwarning("CPDSLib: Unable to notify fetch failure of %d:%016"NL_I64"X, callback not set.", table, key);
return;
}
// get notify function
TPDFetchFailure failureFunc = _FetchFailures[table];
// notify user
failureFunc(key);
}
// Fetch data
void CPDSLib::setupIndexAllocators(NLMISC::IStream &f)
{
std::vector<CIndexAllocator> allocators;
f.serialCont(allocators);
uint i;
for (i=0; i<allocators.size() && i<_Allocators.size(); ++i)
if (_Allocators[i] != NULL)
*(_Allocators[i]) = allocators[i];
}
// Flush acknowledged messages
void CPDSLib::flushAcknowledged(const std::vector<uint32>& ack)
{
uint i;
for (i=0; i<ack.size(); ++i)
{
uint32 msg = ack[i];
TQueuedMessages::iterator it;
for (it=_QueuedMessages.begin(); it!=_QueuedMessages.end() && (*it).first!=msg; )
;
if (it != _QueuedMessages.end())
{
delete (*it).second;
it = _QueuedMessages.erase(it);
}
else
{
++it;
}
}
}
// Flush acknowledged messages until message
void CPDSLib::flushAcknowledged(uint32 ack)
{
// remove all updates before or equal to the acknowledged message
TQueuedMessages::iterator it;
for (it=_QueuedMessages.begin(); it!=_QueuedMessages.end(); )
{
uint32 updateId = (*it).first;
if (updateId <= ack)
{
delete (*it).second;
it = _QueuedMessages.erase(it);
}
else
{
++it;
}
}
}
/*
* Get Message Queue Size
*/
uint CPDSLib::getMessageQueueSize()
{
uint size = 0;
uint i;
for (i=0; i<_QueuedMessages.size(); ++i)
size += _QueuedMessages[i].second->length();
return size;
}
// Erase an object from its table and key
void CPDSLib::erase(TTableIndex table, uint64 key)
{
if (PDVerbose)
nlinfo("CPDSLib: %12s table=%u, key=%016"NL_I64"X", "erase", table, key);
}
// Load a row and its dependent rows from a mapped table
void CPDSLib::load(TTableIndex table, uint64 key)
{
if (PDVerbose)
nlinfo("CPDSLib: %12s table=%u, key=%016"NL_I64"X", "load", table, key);
if (!_UsePDS)
{
if (table >= _FetchFailures.size() || _FetchFailures[table] == NULL)
return;
// get notify function
TPDFetchFailure failureFunc = _FetchFailures[table];
// notify user
failureFunc(key);
}
CDbMessage& msg = nextMessage();
msg.loadRow(table, key);
}
// Release a row
void CPDSLib::release(TTableIndex table, TRowIndex row)
{
if (PDVerbose)
nlinfo("CPDSLib: %12s table=%u, row=%u", "release", table, row);
CDbMessage& msg = nextMessage((uint8)table, row);
msg.releaseRow();
}
// Add a string in pds
void CPDSLib::addString(const NLMISC::CEntityId& eid, const ucstring& str)
{
CDbMessage& msg = nextMessage();
msg.addString(eid.asUint64(), str);
}
// Unmap a string in pds
void CPDSLib::unmapString(const NLMISC::CEntityId& eid)
{
CDbMessage& msg = nextMessage();
msg.unmapString(eid.asUint64());
}
// Get Database Root directory
std::string CPDSLib::getPDSRootDirectory(const std::string& shard, bool wantRemoteDir)
{
std::string dir;
if (PDRootDirectory.get() == "")
{
if (wantRemoteDir)
dir = "pds/";
else
{
// Because we have removed the PD Bsi, but to keep the log analizer wroking,
// we fallback to the path set in the per shard BS because they are
// init with the same value
dir = Bsi.getLocalPath() + "pds/";
//dir = PDBsi.getLocalPath() + "pds/";
}
}
else
{
dir = CPath::standardizePath(PDRootDirectory);
}
std::string findstr("$shard");
std::string::size_type p = dir.find(findstr);
if (p != std::string::npos)
dir.replace(p, findstr.size(), shard);
return dir;
}
// Get Database Root directory
std::string CPDSLib::getRootDirectory(uint databaseId, const std::string& shard, bool wantRemoteDir)
{
std::string dir = getPDSRootDirectory(shard, wantRemoteDir);
return NLMISC::toString("%s%08X/", dir.c_str(), databaseId);
}
// Get Logging directory
std::string CPDSLib::getLogDirectory(uint databaseId, const std::string& shard)
{
return getRootDirectory(databaseId, shard) + "logs/";
}
// Get Lib Logging directory
//std::string CPDSLib::getLogDirectory(const std::string& shard) const
//{
// return getLogDirectory(_DatabaseId, shard);
//}
// Get Logging directory
std::string CPDSLib::getRemoteLogDirectory(uint databaseId, const std::string& shard)
{
return getRootDirectory(databaseId, shard, true) + "logs/";
}
// Get Lib Logging directory
std::string CPDSLib::getRemoteLogDirectory(const std::string& shard) const
{
return getRemoteLogDirectory(_DatabaseId, shard);
}
NLMISC_DYNVARIABLE(uint, CurrentPDQueueSize, "Gives current size of PDS messages to be send")
{
// read or write the variable
if (get)
{
uint i;
uint size = 0;
for (i=0; i<CPDSLib::_Libs.size(); ++i)
if (CPDSLib::_Libs[i] != NULL)
size += CPDSLib::_Libs[i]->getMessageQueueSize();
*pointer = size;
}
}
NLMISC_DYNVARIABLE(uint, EnqueuedPDMessages, "Tells number of messages enqueued and not yet sent")
{
// read or write the variable
if (get)
{
uint i;
uint total = 0;
for (i=0; i<CPDSLib::_Libs.size(); ++i)
if (CPDSLib::_Libs[i] != NULL)
total += CPDSLib::_Libs[i]->enqueuedMessages();
*pointer = total;
}
}
//
//NLMISC_COMMAND(displayPDLogContent, "display pd_log file human readable content", "<databaseId> <filename>")
//{
// if (args.size() != 2)
// return false;
//
// uint databaseId;
// NLMISC::fromString(args[0], databaseId);
// string filename = args[1];
//
// CPDSLib* lib = (databaseId < CPDSLib::_Libs.size() ? CPDSLib::_Libs[databaseId] : NULL);
//
// if (lib == NULL)
// return false;
//
// const std::string& xml = lib->getXmlDescription();
// CDBDescriptionParser desc;
//
// if (!desc.loadDescription((const uint8*)(xml.c_str())))
// {
// nlwarning("Failed to load database %d description", databaseId);
// return false;
// }
//
// if (!desc.buildColumns())
// {
// nlwarning("Failed to build database %d columns for description", databaseId);
// return false;
// }
//
// CIFile ifile;
// CUpdateLog updateLog;
//
// if (!ifile.open(filename))
// {
// nlwarning("Failed to open file '%s'", filename.c_str());
// return false;
// }
//
// try
// {
// updateLog.serial(ifile);
// }
// catch (const Exception& e)
// {
// nlwarning("Failed to load file '%s': %s", filename.c_str(), e.what());
// return false;
// }
//
// updateLog.display(desc, log);
//
// return true;
//}
} // RY_PDS