Refactor NodeStore

This commit is contained in:
Vinnie Falco
2013-07-17 19:52:13 -07:00
parent 5caaea60c9
commit 8b1592e494
27 changed files with 869 additions and 508 deletions

View File

@@ -169,6 +169,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\modules\ripple_app\node\ripple_MdbBackendFactory.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\ripple_app\node\ripple_NodeObject.cpp"> <ClCompile Include="..\..\modules\ripple_app\node\ripple_NodeObject.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -1416,6 +1422,7 @@
<ClInclude Include="..\..\modules\ripple_app\node\ripple_HyperLevelDBBackendFactory.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_HyperLevelDBBackendFactory.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDB.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDB.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_MdbBackendFactory.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_NodeObject.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_NodeObject.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_NodeStore.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_NodeStore.h" />
<ClInclude Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.h" /> <ClInclude Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.h" />

View File

@@ -807,9 +807,6 @@
<ClCompile Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.cpp"> <ClCompile Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.cpp">
<Filter>[1] Ripple\ripple_app\node</Filter> <Filter>[1] Ripple\ripple_app\node</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\modules\ripple_app\node\ripple_NullBackendFactory.cpp">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\ripple_mdb\ripple_mdb.c"> <ClCompile Include="..\..\modules\ripple_mdb\ripple_mdb.c">
<Filter>[1] Ripple\ripple_mdb</Filter> <Filter>[1] Ripple\ripple_mdb</Filter>
</ClCompile> </ClCompile>
@@ -903,6 +900,12 @@
<ClCompile Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.cpp"> <ClCompile Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.cpp">
<Filter>[1] Ripple\ripple_app\node</Filter> <Filter>[1] Ripple\ripple_app\node</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\modules\ripple_app\node\ripple_NullBackendFactory.cpp">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\ripple_app\node\ripple_MdbBackendFactory.cpp">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\Subtrees\sqlite\sqlite3.h"> <ClInclude Include="..\..\Subtrees\sqlite\sqlite3.h">
@@ -1587,9 +1590,6 @@
<ClInclude Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.h"> <ClInclude Include="..\..\modules\ripple_app\node\ripple_LevelDBBackendFactory.h">
<Filter>[1] Ripple\ripple_app\node</Filter> <Filter>[1] Ripple\ripple_app\node</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\modules\ripple_app\node\ripple_NullBackendFactory.h">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\ripple_mdb\ripple_mdb.h"> <ClInclude Include="..\..\modules\ripple_mdb\ripple_mdb.h">
<Filter>[1] Ripple\ripple_mdb</Filter> <Filter>[1] Ripple\ripple_mdb</Filter>
</ClInclude> </ClInclude>
@@ -1686,6 +1686,12 @@
<ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.h"> <ClInclude Include="..\..\modules\ripple_app\node\ripple_KeyvaDBBackendFactory.h">
<Filter>[1] Ripple\ripple_app\node</Filter> <Filter>[1] Ripple\ripple_app\node</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\modules\ripple_app\node\ripple_NullBackendFactory.h">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\ripple_app\node\ripple_MdbBackendFactory.h">
<Filter>[1] Ripple\ripple_app\node</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="..\..\src\cpp\ripple\ripple.proto" /> <CustomBuild Include="..\..\src\cpp\ripple\ripple.proto" />

View File

@@ -10,7 +10,11 @@ Vinnie's Short List (Changes day to day)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
- Replace master lock with
- Replace base_uint and uintXXX with UnsignedInteger - Replace base_uint and uintXXX with UnsignedInteger
* Need to specialize UnsignedInteger to work efficiently with 4 and 8 byte
multiples of the size.
- Rewrite boost program_options in Beast - Rewrite boost program_options in Beast

View File

@@ -283,32 +283,15 @@ const char* WalletDBInit[] =
int WalletDBCount = NUMBER (WalletDBInit); int WalletDBCount = NUMBER (WalletDBInit);
// Hash node database holds nodes indexed by hash // Hash node database holds nodes indexed by hash
const char* HashNodeDBInit[] = // VFALCO TODO Remove this since it looks unused
{ /*
"PRAGMA synchronous=NORMAL;",
"PRAGMA journal_mode=WAL;",
"PRAGMA journal_size_limit=1582080;",
#if (ULONG_MAX > UINT_MAX) && !defined (NO_SQLITE_MMAP)
"PRAGMA mmap_size=171798691840;",
#endif
"BEGIN TRANSACTION;",
"CREATE TABLE CommittedObjects ( \
Hash CHARACTER(64) PRIMARY KEY, \
ObjType CHAR(1) NOT NULL, \
LedgerIndex BIGINT UNSIGNED, \
Object BLOB \
);",
"END TRANSACTION;"
};
int HashNodeDBCount = NUMBER (HashNodeDBInit); int HashNodeDBCount = NUMBER (HashNodeDBInit);
*/
// Net node database holds nodes seen on the network // Net node database holds nodes seen on the network
// XXX Not really used needs replacement. // XXX Not really used needs replacement.
/*
const char* NetNodeDBInit[] = const char* NetNodeDBInit[] =
{ {
"CREATE TABLE KnownNodes ( \ "CREATE TABLE KnownNodes ( \
@@ -320,7 +303,10 @@ const char* NetNodeDBInit[] =
}; };
int NetNodeDBCount = NUMBER (NetNodeDBInit); int NetNodeDBCount = NUMBER (NetNodeDBInit);
*/
// This appears to be unused
/*
const char* PathFindDBInit[] = const char* PathFindDBInit[] =
{ {
"PRAGMA synchronous = OFF; ", "PRAGMA synchronous = OFF; ",
@@ -353,5 +339,5 @@ const char* PathFindDBInit[] =
}; };
int PathFindDBCount = NUMBER (PathFindDBInit); int PathFindDBCount = NUMBER (PathFindDBInit);
*/
// vim:ts=4

View File

@@ -12,19 +12,11 @@ extern const char* RpcDBInit[];
extern const char* TxnDBInit[]; extern const char* TxnDBInit[];
extern const char* LedgerDBInit[]; extern const char* LedgerDBInit[];
extern const char* WalletDBInit[]; extern const char* WalletDBInit[];
extern const char* HashNodeDBInit[];
// VFALCO TODO Figure out what these counts are for // VFALCO TODO Figure out what these counts are for
extern int RpcDBCount; extern int RpcDBCount;
extern int TxnDBCount; extern int TxnDBCount;
extern int LedgerDBCount; extern int LedgerDBCount;
extern int WalletDBCount; extern int WalletDBCount;
extern int HashNodeDBCount;
// VFALCO TODO Seems these two aren't used so delete EVERYTHING.
extern const char* NetNodeDBInit[];
extern const char* PathFindDBInit[];
extern int NetNodeDBCount;
extern int PathFindDBCount;
#endif #endif

View File

@@ -9,8 +9,9 @@
class HyperLevelDBBackendFactory::Backend : public NodeStore::Backend class HyperLevelDBBackendFactory::Backend : public NodeStore::Backend
{ {
public: public:
Backend (StringPairArray const& keyValues) Backend (size_t keyBytes, StringPairArray const& keyValues)
: mName(keyValues ["path"].toStdString ()) : m_keyBytes (keyBytes)
, mName(keyValues ["path"].toStdString ())
, mDB(NULL) , mDB(NULL)
{ {
if (mName.empty()) if (mName.empty())
@@ -58,7 +59,7 @@ public:
{ {
Blob blob (toBlob (obj)); Blob blob (toBlob (obj));
batch.Put ( batch.Put (
hyperleveldb::Slice (reinterpret_cast<char const*>(obj->getHash ().begin ()), 256 / 8), hyperleveldb::Slice (reinterpret_cast<char const*>(obj->getHash ().begin ()), m_keyBytes),
hyperleveldb::Slice (reinterpret_cast<char const*>(&blob.front ()), blob.size ())); hyperleveldb::Slice (reinterpret_cast<char const*>(&blob.front ()), blob.size ()));
} }
return mDB->Write (hyperleveldb::WriteOptions (), &batch).ok (); return mDB->Write (hyperleveldb::WriteOptions (), &batch).ok ();
@@ -68,7 +69,7 @@ public:
{ {
std::string sData; std::string sData;
if (!mDB->Get (hyperleveldb::ReadOptions (), if (!mDB->Get (hyperleveldb::ReadOptions (),
hyperleveldb::Slice (reinterpret_cast<char const*>(hash.begin ()), 256 / 8), &sData).ok ()) hyperleveldb::Slice (reinterpret_cast<char const*>(hash.begin ()), m_keyBytes), &sData).ok ())
{ {
return NodeObject::pointer(); return NodeObject::pointer();
} }
@@ -80,10 +81,10 @@ public:
hyperleveldb::Iterator* it = mDB->NewIterator (hyperleveldb::ReadOptions ()); hyperleveldb::Iterator* it = mDB->NewIterator (hyperleveldb::ReadOptions ());
for (it->SeekToFirst (); it->Valid (); it->Next ()) for (it->SeekToFirst (); it->Valid (); it->Next ())
{ {
if (it->key ().size () == 256 / 8) if (it->key ().size () == m_keyBytes)
{ {
uint256 hash; uint256 hash;
memcpy(hash.begin(), it->key ().data(), 256 / 8); memcpy(hash.begin(), it->key ().data(), m_keyBytes);
func (fromBinary (hash, it->value ().data (), it->value ().size ())); func (fromBinary (hash, it->value ().data (), it->value ().size ()));
} }
} }
@@ -116,6 +117,7 @@ public:
} }
private: private:
size_t const m_keyBytes;
std::string mName; std::string mName;
hyperleveldb::DB* mDB; hyperleveldb::DB* mDB;
}; };
@@ -142,9 +144,9 @@ String HyperLevelDBBackendFactory::getName () const
return "HyperLevelDB"; return "HyperLevelDB";
} }
NodeStore::Backend* HyperLevelDBBackendFactory::createInstance (StringPairArray const& keyValues) NodeStore::Backend* HyperLevelDBBackendFactory::createInstance (size_t keyBytes, StringPairArray const& keyValues)
{ {
return new HyperLevelDBBackendFactory::Backend (keyValues); return new HyperLevelDBBackendFactory::Backend (keyBytes, keyValues);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -23,7 +23,7 @@ public:
static HyperLevelDBBackendFactory& getInstance (); static HyperLevelDBBackendFactory& getInstance ();
String getName () const; String getName () const;
NodeStore::Backend* createInstance (StringPairArray const& keyValues); NodeStore::Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues);
}; };
#endif #endif

View File

@@ -3,6 +3,19 @@
Copyright (c) 2011-2013, OpenCoin, Inc. Copyright (c) 2011-2013, OpenCoin, Inc.
*/ */
//============================================================================== //==============================================================================
/*
TODO
- Check consistency / range checking on read
- Cache top level tree nodes
- Coalesce I/O in RandomAccessFile
- Delete / file compaction
*/
class KeyvaDBImp : public KeyvaDB class KeyvaDBImp : public KeyvaDB
{ {
@@ -336,10 +349,10 @@ public:
bool get (void const* key, GetCallback* callback) bool get (void const* key, GetCallback* callback)
{ {
// VFALCO TODD Swap these two lines
SharedState::WriteAccess state (m_state);
FindResult findResult (m_keyStorage.getData ()); FindResult findResult (m_keyStorage.getData ());
SharedState::WriteAccess state (m_state);
bool found = false; bool found = false;
if (state->hasKeys ()) if (state->hasKeys ())
@@ -348,7 +361,7 @@ public:
if (found) if (found)
{ {
void* const destStorage = callback->createStorageForValue (findResult.keyRecord.valSize); void* const destStorage = callback->getStorageForValue (findResult.keyRecord.valSize);
RandomAccessFileInputStream stream (state->valFile); RandomAccessFileInputStream stream (state->valFile);
@@ -536,7 +549,7 @@ public:
{ {
} }
void* createStorageForValue (int valueBytes) void* getStorageForValue (int valueBytes)
{ {
bassert (valueBytes <= maxPayloadBytes); bassert (valueBytes <= maxPayloadBytes);

View File

@@ -15,7 +15,7 @@ public:
class GetCallback class GetCallback
{ {
public: public:
virtual void* createStorageForValue (int valueBytes) = 0; virtual void* getStorageForValue (int valueBytes) = 0;
}; };
static KeyvaDB* New (int keyBytes, static KeyvaDB* New (int keyBytes,
@@ -25,8 +25,13 @@ public:
virtual ~KeyvaDB () { } virtual ~KeyvaDB () { }
// VFALCO TODO Make the return value a Result so we can
// detect corruption and errors!
//
virtual bool get (void const* key, GetCallback* callback) = 0; virtual bool get (void const* key, GetCallback* callback) = 0;
// VFALCO TODO Use Result for return value
//
virtual void put (void const* key, void const* value, int valueBytes) = 0; virtual void put (void const* key, void const* value, int valueBytes) = 0;
virtual void flush () = 0; virtual void flush () = 0;

View File

@@ -7,15 +7,9 @@
class KeyvaDBBackendFactory::Backend : public NodeStore::Backend class KeyvaDBBackendFactory::Backend : public NodeStore::Backend
{ {
public: public:
typedef UnsignedInteger <32> Key; Backend (size_t keyBytes, StringPairArray const& keyValues)
: m_keyBytes (keyBytes)
enum , m_path (keyValues ["path"])
{
keyBytes = Key::sizeInBytes
};
explicit Backend (StringPairArray const& keyValues)
: m_path (keyValues ["path"])
, m_db (KeyvaDB::New ( , m_db (KeyvaDB::New (
keyBytes, keyBytes,
File::getCurrentWorkingDirectory().getChildFile (m_path).withFileExtension ("key"), File::getCurrentWorkingDirectory().getChildFile (m_path).withFileExtension ("key"),
@@ -33,6 +27,48 @@ public:
return m_path.toStdString (); return m_path.toStdString ();
} }
//--------------------------------------------------------------------------
Status get (void const* key, GetCallback* callback)
{
Status status (ok);
struct ForwardingGetCallback : KeyvaDB::GetCallback
{
ForwardingGetCallback (Backend::GetCallback* callback)
: m_callback (callback)
{
}
void* getStorageForValue (int valueBytes)
{
return m_callback->getStorageForValue (valueBytes);
}
private:
Backend::GetCallback* const m_callback;
};
ForwardingGetCallback cb (callback);
// VFALCO TODO Can't we get KeyvaDB to provide a proper status?
//
bool const found = m_db->get (key, &cb);
if (found)
{
status = ok;
}
else
{
status = notFound;
}
return status;
}
//--------------------------------------------------------------------------
void writeObject (NodeObject::ref object) void writeObject (NodeObject::ref object)
{ {
Blob blob (toBlob (object)); Blob blob (toBlob (object));
@@ -54,7 +90,7 @@ public:
int valueBytes; int valueBytes;
HeapBlock <char> data; HeapBlock <char> data;
void* createStorageForValue (int valueBytes_) void* getStorageForValue (int valueBytes_)
{ {
valueBytes = valueBytes_; valueBytes = valueBytes_;
@@ -112,6 +148,7 @@ public:
} }
private: private:
size_t const m_keyBytes;
String m_path; String m_path;
ScopedPointer <KeyvaDB> m_db; ScopedPointer <KeyvaDB> m_db;
}; };
@@ -138,9 +175,9 @@ String KeyvaDBBackendFactory::getName () const
return "KeyvaDB"; return "KeyvaDB";
} }
NodeStore::Backend* KeyvaDBBackendFactory::createInstance (StringPairArray const& keyValues) NodeStore::Backend* KeyvaDBBackendFactory::createInstance (size_t keyBytes, StringPairArray const& keyValues)
{ {
return new KeyvaDBBackendFactory::Backend (keyValues); return new KeyvaDBBackendFactory::Backend (keyBytes, keyValues);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -21,7 +21,7 @@ public:
static KeyvaDBBackendFactory& getInstance (); static KeyvaDBBackendFactory& getInstance ();
String getName () const; String getName () const;
NodeStore::Backend* createInstance (StringPairArray const& keyValues); NodeStore::Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues);
}; };
#endif #endif

View File

@@ -7,11 +7,12 @@
class LevelDBBackendFactory::Backend : public NodeStore::Backend class LevelDBBackendFactory::Backend : public NodeStore::Backend
{ {
public: public:
Backend (StringPairArray const& keyValues) Backend (int keyBytes, StringPairArray const& keyValues)
: mName(keyValues ["path"].toStdString ()) : m_keyBytes (keyBytes)
, mDB(NULL) , m_name(keyValues ["path"].toStdString ())
, m_db(NULL)
{ {
if (mName.empty()) if (m_name.empty())
throw std::runtime_error ("Missing path in LevelDB backend"); throw std::runtime_error ("Missing path in LevelDB backend");
leveldb::Options options; leveldb::Options options;
@@ -33,21 +34,83 @@ public:
if (!keyValues["open_files"].isEmpty()) if (!keyValues["open_files"].isEmpty())
options.max_open_files = keyValues["open_files"].getIntValue(); options.max_open_files = keyValues["open_files"].getIntValue();
leveldb::Status status = leveldb::DB::Open (options, mName, &mDB); leveldb::Status status = leveldb::DB::Open (options, m_name, &m_db);
if (!status.ok () || !mDB) if (!status.ok () || !m_db)
throw (std::runtime_error (std::string("Unable to open/create leveldb: ") + status.ToString())); throw (std::runtime_error (std::string("Unable to open/create leveldb: ") + status.ToString()));
} }
~Backend () ~Backend ()
{ {
delete mDB; delete m_db;
} }
std::string getDataBaseName() std::string getDataBaseName()
{ {
return mName; return m_name;
} }
//--------------------------------------------------------------------------
struct StdString
{
std::string blob;
};
typedef RecycledObjectPool <StdString> StdStringPool;
//--------------------------------------------------------------------------
Status get (void const* key, GetCallback* callback)
{
Status status (ok);
leveldb::ReadOptions const options;
leveldb::Slice const slice (static_cast <char const*> (key), m_keyBytes);
{
// These are reused std::string objects,
// required for leveldb's funky interface.
//
StdStringPool::ScopedItem item (m_stringPool);
std::string& blob = item.getObject ().blob;
leveldb::Status getStatus = m_db->Get (options, slice, &blob);
if (getStatus.ok ())
{
void* const buffer = callback->getStorageForValue (blob.size ());
if (buffer != nullptr)
{
memcpy (buffer, blob.data (), blob.size ());
}
else
{
Throw (std::bad_alloc ());
}
}
else
{
if (getStatus.IsCorruption ())
{
status = dataCorrupt;
}
else if (getStatus.IsNotFound ())
{
status = notFound;
}
else
{
status = unknown;
}
}
}
return status;
}
//--------------------------------------------------------------------------
bool bulkStore (const std::vector< NodeObject::pointer >& objs) bool bulkStore (const std::vector< NodeObject::pointer >& objs)
{ {
leveldb::WriteBatch batch; leveldb::WriteBatch batch;
@@ -56,17 +119,17 @@ public:
{ {
Blob blob (toBlob (obj)); Blob blob (toBlob (obj));
batch.Put ( batch.Put (
leveldb::Slice (reinterpret_cast<char const*>(obj->getHash ().begin ()), 256 / 8), leveldb::Slice (reinterpret_cast<char const*>(obj->getHash ().begin ()), m_keyBytes),
leveldb::Slice (reinterpret_cast<char const*>(&blob.front ()), blob.size ())); leveldb::Slice (reinterpret_cast<char const*>(&blob.front ()), blob.size ()));
} }
return mDB->Write (leveldb::WriteOptions (), &batch).ok (); return m_db->Write (leveldb::WriteOptions (), &batch).ok ();
} }
NodeObject::pointer retrieve (uint256 const& hash) NodeObject::pointer retrieve (uint256 const& hash)
{ {
std::string sData; std::string sData;
if (!mDB->Get (leveldb::ReadOptions (), if (!m_db->Get (leveldb::ReadOptions (),
leveldb::Slice (reinterpret_cast<char const*>(hash.begin ()), 256 / 8), &sData).ok ()) leveldb::Slice (reinterpret_cast<char const*>(hash.begin ()), m_keyBytes), &sData).ok ())
{ {
return NodeObject::pointer(); return NodeObject::pointer();
} }
@@ -75,15 +138,20 @@ public:
void visitAll (FUNCTION_TYPE<void (NodeObject::pointer)> func) void visitAll (FUNCTION_TYPE<void (NodeObject::pointer)> func)
{ {
leveldb::Iterator* it = mDB->NewIterator (leveldb::ReadOptions ()); leveldb::Iterator* it = m_db->NewIterator (leveldb::ReadOptions ());
for (it->SeekToFirst (); it->Valid (); it->Next ()) for (it->SeekToFirst (); it->Valid (); it->Next ())
{ {
if (it->key ().size () == 256 / 8) if (it->key ().size () == m_keyBytes)
{ {
uint256 hash; uint256 hash;
memcpy(hash.begin(), it->key ().data(), 256 / 8); memcpy(hash.begin(), it->key ().data(), m_keyBytes);
func (fromBinary (hash, it->value ().data (), it->value ().size ())); func (fromBinary (hash, it->value ().data (), it->value ().size ()));
} }
else
{
// VFALCO NOTE What does it mean to find an
// incorrectly sized key? Corruption?
}
} }
} }
@@ -114,8 +182,10 @@ public:
} }
private: private:
std::string mName; size_t const m_keyBytes;
leveldb::DB* mDB; StdStringPool m_stringPool;
std::string m_name;
leveldb::DB* m_db;
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -140,9 +210,9 @@ String LevelDBBackendFactory::getName () const
return "LevelDB"; return "LevelDB";
} }
NodeStore::Backend* LevelDBBackendFactory::createInstance (StringPairArray const& keyValues) NodeStore::Backend* LevelDBBackendFactory::createInstance (size_t keyBytes, StringPairArray const& keyValues)
{ {
return new LevelDBBackendFactory::Backend (keyValues); return new LevelDBBackendFactory::Backend (keyBytes, keyValues);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -21,7 +21,7 @@ public:
static LevelDBBackendFactory& getInstance (); static LevelDBBackendFactory& getInstance ();
String getName () const; String getName () const;
NodeStore::Backend* createInstance (StringPairArray const& keyValues); NodeStore::Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues);
}; };
#endif #endif

View File

@@ -6,6 +6,8 @@
SETUP_LOG (NodeObject) SETUP_LOG (NodeObject)
//------------------------------------------------------------------------------
NodeObject::NodeObject ( NodeObject::NodeObject (
NodeObjectType type, NodeObjectType type,
LedgerIndex ledgerIndex, LedgerIndex ledgerIndex,
@@ -32,80 +34,6 @@ NodeObject::NodeObject (
{ {
} }
NodeObject::NodeObject (void const* key, void const* value, int valueBytes)
{
DecodedBlob decoded (key, value, valueBytes);
if (decoded.success)
{
mType = decoded.objectType;
mHash = uint256 (key);
mLedgerIndex = decoded.ledgerIndex;
mData = Blob (decoded.objectData, decoded.objectData + decoded.dataBytes);
}
else
{
// VFALCO TODO Write the hex version of key to the string for diagnostics.
String s;
s << "NodeStore:: DecodedBlob failed";
Throw (s);
}
}
NodeObject::DecodedBlob::DecodedBlob (void const* key, void const* value, int valueBytes)
{
/* Data format:
Bytes
0...3 LedgerIndex 32-bit big endian integer
4...7 Unused? An unused copy of the LedgerIndex
8 char One of NodeObjectType
9...end The body of the object data
*/
success = false;
key = key;
// VFALCO NOTE Ledger indexes should have started at 1
ledgerIndex = LedgerIndex (-1);
objectType = hotUNKNOWN;
objectData = nullptr;
dataBytes = bmin (0, valueBytes - 9);
if (dataBytes > 4)
{
LedgerIndex const* index = static_cast <LedgerIndex const*> (value);
ledgerIndex = ByteOrder::swapIfLittleEndian (*index);
}
// VFALCO NOTE What about bytes 4 through 7 inclusive?
if (dataBytes > 8)
{
unsigned char const* byte = static_cast <unsigned char const*> (value);
objectType = static_cast <NodeObjectType> (byte [8]);
}
if (dataBytes > 9)
{
objectData = static_cast <unsigned char const*> (value) + 9;
switch (objectType)
{
case hotUNKNOWN:
default:
break;
case hotLEDGER:
case hotTRANSACTION:
case hotACCOUNT_NODE:
case hotTRANSACTION_NODE:
success = true;
break;
}
}
}
NodeObjectType NodeObject::getType () const NodeObjectType NodeObject::getType () const
{ {
return mType; return mType;
@@ -125,3 +53,32 @@ Blob const& NodeObject::getData () const
{ {
return mData; return mData;
} }
bool NodeObject::isCloneOf (NodeObject const& other) const
{
return
mType == other.mType &&
mHash == other.mHash &&
mLedgerIndex == other.mLedgerIndex &&
mData == other.mData
;
}
//------------------------------------------------------------------------------
class NodeObjectTests : public UnitTest
{
public:
NodeObjectTests () : UnitTest ("NodeObject")
{
}
void runTest ()
{
}
};
static NodeObjectTests nodeObjectTests;

View File

@@ -64,40 +64,6 @@ public:
int bytesInBuffer, int bytesInBuffer,
uint256 const & hash); uint256 const & hash);
/** Create from a key/value blob.
This is the format in which a NodeObject is stored in the
persistent storage layer.
@see NodeStore
*/
NodeObject (void const* key, void const* value, int valueBytes);
/** Parsed key/value blob into NodeObject components.
This will extract the information required to construct
a NodeObject. It also does consistency checking and returns
the result, so it is possible to determine if the data
is corrupted without throwing an exception. Note all forms
of corruption are detected so further analysis will be
needed to eliminate false positives.
This is the format in which a NodeObject is stored in the
persistent storage layer.
*/
struct DecodedBlob
{
DecodedBlob (void const* key, void const* value, int valueBytes);
bool success;
void const* key;
LedgerIndex ledgerIndex;
NodeObjectType objectType;
unsigned char const* objectData;
int dataBytes;
};
/** Retrieve the type of this object. /** Retrieve the type of this object.
*/ */
NodeObjectType getType () const; NodeObjectType getType () const;
@@ -115,6 +81,10 @@ public:
*/ */
Blob const& getData () const; Blob const& getData () const;
/** See if this object has the same data as another object.
*/
bool isCloneOf (NodeObject const& other) const;
private: private:
NodeObjectType mType; NodeObjectType mType;
uint256 mHash; uint256 mHash;

View File

@@ -95,220 +95,519 @@ int NodeStore::Backend::getWriteLoad ()
// NodeStore // NodeStore
// //
Array <NodeStore::BackendFactory*> NodeStore::s_factories; class NodeStoreImp : public NodeStore
NodeStore::NodeStore (String backendParameters,
String fastBackendParameters,
int cacheSize,
int cacheAge)
: m_backend (createBackend (backendParameters))
, m_fastBackend (fastBackendParameters.isNotEmpty () ? createBackend (fastBackendParameters)
: nullptr)
, m_cache ("NodeStore", cacheSize, cacheAge)
, m_negativeCache ("NoteStoreNegativeCache", 0, 120)
{ {
} public:
/** Size of a key.
void NodeStore::addBackendFactory (BackendFactory& factory) */
{ enum
s_factories.add (&factory);
}
float NodeStore::getCacheHitRate ()
{
return m_cache.getHitRate ();
}
void NodeStore::tune (int size, int age)
{
m_cache.setTargetSize (size);
m_cache.setTargetAge (age);
}
void NodeStore::sweep ()
{
m_cache.sweep ();
m_negativeCache.sweep ();
}
void NodeStore::waitWrite ()
{
m_backend->waitWrite ();
if (m_fastBackend)
m_fastBackend->waitWrite ();
}
int NodeStore::getWriteLoad ()
{
return m_backend->getWriteLoad ();
}
bool NodeStore::store (NodeObjectType type, uint32 index,
Blob const& data, uint256 const& hash)
{
bool wasStored = false;
bool const keyFoundAndObjectCached = m_cache.refreshIfPresent (hash);
// VFALCO NOTE What happens if the key is found, but the object
// fell out of the cache? We will end up passing it
// to the backend anyway.
//
if (! keyFoundAndObjectCached)
{ {
keyBytes = 32
};
// VFALCO TODO Rename this to RIPPLE_NODESTORE_VERIFY_HASHES and make /** Parsed key/value blob into NodeObject components.
// it be 1 or 0 instead of merely defined or undefined.
//
#ifdef PARANOID
assert (hash == Serializer::getSHA512Half (data));
#endif
NodeObject::pointer object = boost::make_shared<NodeObject> (type, index, data, hash); This will extract the information required to construct
a NodeObject. It also does consistency checking and returns
the result, so it is possible to determine if the data
is corrupted without throwing an exception. Note all forms
of corruption are detected so further analysis will be
needed to eliminate false positives.
// VFALCO NOTE What does it mean to canonicalize an object? This is the format in which a NodeObject is stored in the
// persistent storage layer.
if (!m_cache.canonicalize (hash, object)) */
struct DecodedBlob
{
/** Construct the decoded blob from raw data.
The `success` member will indicate if the operation was succesful.
*/
DecodedBlob (void const* keyParam, void const* value, int valueBytes)
{ {
m_backend->store (object); /* Data format:
if (m_fastBackend) Bytes
m_fastBackend->store (object);
0...3 LedgerIndex 32-bit big endian integer
4...7 Unused? An unused copy of the LedgerIndex
8 char One of NodeObjectType
9...end The body of the object data
*/
success = false;
key = keyParam;
// VFALCO NOTE Ledger indexes should have started at 1
ledgerIndex = LedgerIndex (-1);
objectType = hotUNKNOWN;
objectData = nullptr;
dataBytes = bmax (0, valueBytes - 9);
if (dataBytes > 4)
{
LedgerIndex const* index = static_cast <LedgerIndex const*> (value);
ledgerIndex = ByteOrder::swapIfLittleEndian (*index);
}
// VFALCO NOTE What about bytes 4 through 7 inclusive?
if (dataBytes > 8)
{
unsigned char const* byte = static_cast <unsigned char const*> (value);
objectType = static_cast <NodeObjectType> (byte [8]);
}
if (dataBytes > 9)
{
objectData = static_cast <unsigned char const*> (value) + 9;
switch (objectType)
{
case hotUNKNOWN:
default:
break;
case hotLEDGER:
case hotTRANSACTION:
case hotACCOUNT_NODE:
case hotTRANSACTION_NODE:
success = true;
break;
}
}
} }
m_negativeCache.del (hash); /** Create a NodeObject from this data.
*/
wasStored = true; NodeObject::pointer createObject ()
}
return wasStored;
}
NodeObject::pointer NodeStore::retrieve (uint256 const& hash)
{
NodeObject::pointer obj = m_cache.fetch (hash);
if (obj || m_negativeCache.isPresent (hash))
return obj;
if (m_fastBackend)
{
obj = retrieve (m_fastBackend, hash);
if (obj)
{ {
m_cache.canonicalize (hash, obj); NodeObject::pointer object;
return obj;
if (success)
{
// VFALCO NOTE I dislke these shared pointers from boost
object = boost::make_shared <NodeObject> (
objectType, ledgerIndex, objectData, dataBytes, uint256 (key));
}
return object;
} }
bool success;
void const* key;
LedgerIndex ledgerIndex;
NodeObjectType objectType;
unsigned char const* objectData;
int dataBytes;
};
//--------------------------------------------------------------------------
class EncodedBlob
{
HeapBlock <char> data;
};
public:
NodeStoreImp (String backendParameters,
String fastBackendParameters,
int cacheSize,
int cacheAge)
: m_backend (createBackend (backendParameters))
, m_fastBackend (fastBackendParameters.isNotEmpty () ? createBackend (fastBackendParameters)
: nullptr)
, m_cache ("NodeStore", cacheSize, cacheAge)
, m_negativeCache ("NoteStoreNegativeCache", 0, 120)
{
} }
~NodeStoreImp ()
{ {
// m_hooks->onRetrieveBegin () // VFALCO NOTE This shouldn't be necessary, the backend can
// just handle it in the destructor.
// VFALCO TODO Why is this an autoptr? Why can't it just be a plain old object?
// //
LoadEvent::autoptr event (getApp().getJobQueue ().getLoadEventAP (jtHO_READ, "HOS::retrieve")); m_backend->waitWrite ();
obj = retrieve (m_backend, hash); if (m_fastBackend)
m_fastBackend->waitWrite ();
}
float getCacheHitRate ()
{
return m_cache.getHitRate ();
}
void tune (int size, int age)
{
m_cache.setTargetSize (size);
m_cache.setTargetAge (age);
}
void sweep ()
{
m_cache.sweep ();
m_negativeCache.sweep ();
}
int getWriteLoad ()
{
return m_backend->getWriteLoad ();
}
bool store (NodeObjectType type,
uint32 index,
Blob const& data,
uint256 const& hash)
{
bool wasStored = false;
bool const keyFoundAndObjectCached = m_cache.refreshIfPresent (hash);
// VFALCO NOTE What happens if the key is found, but the object
// fell out of the cache? We will end up passing it
// to the backend anyway.
//
if (! keyFoundAndObjectCached)
{
// VFALCO TODO Rename this to RIPPLE_NODESTORE_VERIFY_HASHES and make
// it be 1 or 0 instead of merely defined or undefined.
//
#ifdef PARANOID
assert (hash == Serializer::getSHA512Half (data));
#endif
NodeObject::pointer object = boost::make_shared <NodeObject> (type, index, data, hash);
// VFALCO NOTE What does it mean to canonicalize an object?
//
if (!m_cache.canonicalize (hash, object))
{
m_backend->store (object);
if (m_fastBackend)
m_fastBackend->store (object);
}
m_negativeCache.del (hash);
wasStored = true;
}
return wasStored;
}
//------------------------------------------------------------------------------
NodeObject::pointer retrieve (uint256 const& hash)
{
// See if the object already exists in the cache
//
NodeObject::pointer obj = m_cache.fetch (hash);
if (obj == nullptr) if (obj == nullptr)
{ {
m_negativeCache.add (hash); // It's not in the cache, see if we can skip checking the db.
//
if (! m_negativeCache.isPresent (hash))
{
// There's still a chance it could be in one of the databases.
// VFALCO TODO Eliminate return from middle of function bool foundInFastBackend = false;
return obj; // VFALCO NOTE This is nullptr, why return obj? // Check the fast backend database if we have one
//
if (m_fastBackend != nullptr)
{
obj = retrieveInternal (m_fastBackend, hash);
// If we found the object, avoid storing it again later.
if (obj != nullptr)
foundInFastBackend = true;
}
// Are we still without an object?
//
if (obj == nullptr)
{
// Yes so at last we will try the main database.
//
{
// Monitor this operation's load since it is expensive.
// m_hooks->onRetrieveBegin ()
// VFALCO TODO Why is this an autoptr? Why can't it just be a plain old object?
//
LoadEvent::autoptr event (getApp().getJobQueue ().getLoadEventAP (jtHO_READ, "HOS::retrieve"));
obj = retrieveInternal (m_backend, hash);
// m_hooks->onRetrieveEnd ()
}
// If it's not in the main database, remember that so we
// can skip the lookup for the same object again later.
//
if (obj == nullptr)
m_negativeCache.add (hash);
}
// Did we finally get something?
//
if (obj != nullptr)
{
// Yes it so canonicalize. This solves the problem where
// more than one thread has its own copy of the same object.
//
m_cache.canonicalize (hash, obj);
if (! foundInFastBackend)
{
// If we have a fast back end, store it there for later.
//
if (m_fastBackend != nullptr)
m_fastBackend->store (obj);
// Since this was a 'hard' fetch, we will log it.
//
WriteLog (lsTRACE, NodeObject) << "HOS: " << hash << " fetch: in db";
}
}
}
else
{
// hash is known not to be in the database
}
}
else
{
// found it!
} }
return obj;
} }
// VFALCO NOTE What does this do? NodeObject::pointer retrieveInternal (Backend* backend, uint256 const& hash)
m_cache.canonicalize (hash, obj); {
// VFALCO TODO Make this not allocate and free on each call
//
struct MyGetCallback : Backend::GetCallback
{
void* getStorageForValue (size_t sizeInBytes)
{
bytes = sizeInBytes;
data.malloc (sizeInBytes);
if (m_fastBackend) return &data [0];
m_fastBackend->store(obj); }
WriteLog (lsTRACE, NodeObject) << "HOS: " << hash << " fetch: in db"; size_t bytes;
HeapBlock <char> data;
};
return obj; NodeObject::pointer object;
MyGetCallback cb;
Backend::Status const status = backend->get (hash.begin (), &cb);
if (status == Backend::ok)
{
// Deserialize the payload into its components.
//
DecodedBlob decoded (hash.begin (), cb.data.getData (), cb.bytes);
if (decoded.success)
{
object = decoded.createObject ();
}
else
{
// Houston, we've had a problem. Data is likely corrupt.
// VFALCO TODO Deal with encountering corrupt data!
WriteLog (lsFATAL, NodeObject) << "Corrupt NodeObject #" << hash;
}
}
return object;
}
//------------------------------------------------------------------------------
void importVisitor (
std::vector <NodeObject::pointer>& objects,
NodeObject::pointer object)
{
if (objects.size() >= bulkWriteBatchSize)
{
m_backend->bulkStore (objects);
objects.clear ();
objects.reserve (bulkWriteBatchSize);
}
objects.push_back (object);
}
int import (String sourceBackendParameters)
{
ScopedPointer <NodeStore::Backend> srcBackend (createBackend (sourceBackendParameters));
WriteLog (lsWARNING, NodeObject) <<
"Node import from '" << srcBackend->getDataBaseName() << "' to '"
<< m_backend->getDataBaseName() << "'.";
std::vector <NodeObject::pointer> objects;
objects.reserve (bulkWriteBatchSize);
srcBackend->visitAll (BIND_TYPE (&NodeStoreImp::importVisitor, this, boost::ref (objects), P_1));
if (!objects.empty ())
m_backend->bulkStore (objects);
return 0;
}
NodeStore::Backend* createBackend (String const& parameters)
{
Backend* backend = nullptr;
StringPairArray keyValues = parseKeyValueParameters (parameters, '|');
String const& type = keyValues ["type"];
if (type.isNotEmpty ())
{
BackendFactory* factory = nullptr;
for (int i = 0; i < s_factories.size (); ++i)
{
if (s_factories [i]->getName () == type)
{
factory = s_factories [i];
break;
}
}
if (factory != nullptr)
{
backend = factory->createInstance (keyBytes, keyValues);
}
else
{
throw std::runtime_error ("unkown backend type");
}
}
else
{
throw std::runtime_error ("missing backend type");
}
return backend;
}
static void addBackendFactory (BackendFactory& factory)
{
s_factories.add (&factory);
}
private:
static Array <NodeStore::BackendFactory*> s_factories;
RecycledObjectPool <EncodedBlob> m_blobPool;
// Persistent key/value storage.
ScopedPointer <Backend> m_backend;
// Larger key/value storage, but not necessarily persistent.
ScopedPointer <Backend> m_fastBackend;
// VFALCO NOTE What are these things for? We need comments.
TaggedCache <uint256, NodeObject, UptimeTimerAdapter> m_cache;
KeyCache <uint256, UptimeTimerAdapter> m_negativeCache;
};
Array <NodeStore::BackendFactory*> NodeStoreImp::s_factories;
//------------------------------------------------------------------------------
void NodeStore::addBackendFactory (BackendFactory& factory)
{
NodeStoreImp::addBackendFactory (factory);
}
NodeStore* NodeStore::New (String backendParameters,
String fastBackendParameters,
int cacheSize,
int cacheAge)
{
return new NodeStoreImp (backendParameters,
fastBackendParameters,
cacheSize,
cacheAge);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
NodeObject::pointer NodeStore::retrieve (Backend* backend, uint256 const& hash) class NodeStoreTests : public UnitTest
{ {
return backend->retrieve (hash); public:
} enum
void NodeStore::importVisitor (
std::vector <NodeObject::pointer>& objects,
NodeObject::pointer object)
{
if (objects.size() >= bulkWriteBatchSize)
{ {
m_backend->bulkStore (objects); maxPayloadBytes = 1000,
objects.clear (); numObjects = 1000
objects.reserve (bulkWriteBatchSize); };
NodeStoreTests () : UnitTest ("NodeStore")
{
} }
objects.push_back (object); // Create a pseudo-random object
} static NodeObject* createNodeObject (int index, int64 seedValue, HeapBlock <char>& payloadBuffer)
int NodeStore::import (String sourceBackendParameters)
{
ScopedPointer <NodeStore::Backend> srcBackend (createBackend (sourceBackendParameters));
WriteLog (lsWARNING, NodeObject) <<
"Node import from '" << srcBackend->getDataBaseName() << "' to '"
<< m_backend->getDataBaseName() << "'.";
std::vector <NodeObject::pointer> objects;
objects.reserve (bulkWriteBatchSize);
srcBackend->visitAll (BIND_TYPE (&NodeStore::importVisitor, this, boost::ref (objects), P_1));
if (!objects.empty ())
m_backend->bulkStore (objects);
return 0;
}
NodeStore::Backend* NodeStore::createBackend (String const& parameters)
{
Backend* backend = nullptr;
StringPairArray keyValues = parseKeyValueParameters (parameters, '|');
String const& type = keyValues ["type"];
if (type.isNotEmpty ())
{ {
BackendFactory* factory = nullptr; Random r (seedValue + index);
for (int i = 0; i < s_factories.size (); ++i) NodeObjectType type;
switch (r.nextInt (4))
{ {
if (s_factories [i]->getName () == type) case 0: type = hotLEDGER; break;
{ case 1: type = hotTRANSACTION; break;
factory = s_factories [i]; case 2: type = hotACCOUNT_NODE; break;
break; case 3: type = hotTRANSACTION_NODE; break;
} default:
} type = hotUNKNOWN;
break;
};
if (factory != nullptr) LedgerIndex ledgerIndex = 1 + r.nextInt (1024 * 1024);
{
backend = factory->createInstance (keyValues); uint256 hash;
} r.nextBlob (hash.begin (), hash.size ());
else
{ int payloadBytes = 1 + r.nextInt (maxPayloadBytes);
throw std::runtime_error ("unkown backend type"); r.nextBlob (payloadBuffer.getData (), payloadBytes);
}
} return new NodeObject (type, ledgerIndex, payloadBuffer.getData (), payloadBytes, hash);
else
{
throw std::runtime_error ("missing backend type");
} }
return backend; void runTest ()
} {
beginTest ("create");
int64 const seedValue = 50;
HeapBlock <char> payloadBuffer (maxPayloadBytes);
for (int i = 0; i < numObjects; ++i)
{
ScopedPointer <NodeObject> object (createNodeObject (i, seedValue, payloadBuffer));
}
}
};
static NodeStoreTests nodeStoreTests;

View File

@@ -34,14 +34,55 @@ public:
}; };
/** Back end used for the store. /** Back end used for the store.
A Backend implements a persistent key/value storage system.
Keys sizes are all fixed within the same database.
*/ */
class Backend class Backend
{ {
public: public:
/** Return codes from operations.
*/
enum Status
{
ok,
notFound,
dataCorrupt,
unknown
};
Backend (); Backend ();
virtual ~Backend () { } virtual ~Backend () { }
/** Provides storage for retrieved objects.
*/
struct GetCallback
{
/** Get storage for an object.
@param sizeInBytes The number of bytes needed to store the value.
@return A pointer to a buffer large enough to hold all the bytes.
*/
virtual void* getStorageForValue (size_t sizeInBytes) = 0;
};
/** Retrieve a single object.
If the object is not found or an error is encountered, the
result will indicate the condition.
@param key A pointer to the key data.
@param callback The callback used to obtain storage for the value.
@return The result of the operation.
*/
virtual Status get (void const* key, GetCallback* callback) { return notFound; }
/** Store a single object. /** Store a single object.
*/ */
// VFALCO TODO Why should the Backend know or care about NodeObject? // VFALCO TODO Why should the Backend know or care about NodeObject?
@@ -54,13 +95,6 @@ public:
*/ */
virtual NodeObject::pointer retrieve (uint256 const &hash) = 0; virtual NodeObject::pointer retrieve (uint256 const &hash) = 0;
struct GetCallback
{
virtual void* getBufferForValue (int valueBytes) = 0;
};
virtual bool get (void const* key, GetCallback* callback) { return false; }
// Visit every object in the database // Visit every object in the database
// This function will only be called during an import operation // This function will only be called during an import operation
// //
@@ -69,7 +103,7 @@ public:
virtual void visitAll (FUNCTION_TYPE <void (NodeObject::pointer)>) = 0; virtual void visitAll (FUNCTION_TYPE <void (NodeObject::pointer)>) = 0;
private: private:
friend class NodeStore; friend class NodeStoreImp;
// VFALCO TODO Put this bulk writing logic into a separate class. // VFALCO TODO Put this bulk writing logic into a separate class.
// NOTE Why are these virtual? // NOTE Why are these virtual?
@@ -115,8 +149,13 @@ public:
virtual String getName () const = 0; virtual String getName () const = 0;
/** Create an instance of this factory's backend. /** Create an instance of this factory's backend.
@param keyBytes The fixed number of bytes per key.
@param keyValues A set of key/value configuration pairs.
@return A pointer to the Backend object.
*/ */
virtual Backend* createInstance (StringPairArray const& keyValues) = 0; virtual Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues) = 0;
}; };
public: public:
@@ -133,10 +172,10 @@ public:
// Is cacheAge in minutes? seconds? // Is cacheAge in minutes? seconds?
// These should be in the parameters. // These should be in the parameters.
// //
NodeStore (String backendParameters, static NodeStore* New (String backendParameters,
String fastBackendParameters, String fastBackendParameters,
int cacheSize, int cacheSize,
int cacheAge); int cacheAge);
/** Add the specified backend factory to the list of available factories. /** Add the specified backend factory to the list of available factories.
@@ -146,52 +185,31 @@ public:
static void addBackendFactory (BackendFactory& factory); static void addBackendFactory (BackendFactory& factory);
// VFALCO TODO Document this. // VFALCO TODO Document this.
float getCacheHitRate (); virtual float getCacheHitRate () = 0;
// VFALCO TODO Document this. // VFALCO TODO Document this.
bool store (NodeObjectType type, uint32 index, Blob const& data, virtual bool store (NodeObjectType type, uint32 index, Blob const& data,
uint256 const& hash); uint256 const& hash) = 0;
// VFALCO TODO Document this. // VFALCO TODO Document this.
NodeObject::pointer retrieve (uint256 const& hash); // TODO Replace uint256 with void*
//
// VFALCO TODO Document this. virtual NodeObject::pointer retrieve (uint256 const& hash) = 0;
void waitWrite ();
// VFALCO TODO Document this. // VFALCO TODO Document this.
// TODO Document the parameter meanings. // TODO Document the parameter meanings.
void tune (int size, int age); virtual void tune (int size, int age) = 0;
// VFALCO TODO Document this. // VFALCO TODO Document this.
void sweep (); virtual void sweep () = 0;
// VFALCO TODO Document this. // VFALCO TODO Document this.
// What are the units of the return value? // What are the units of the return value?
int getWriteLoad (); virtual int getWriteLoad () = 0;
// VFALCO TODO Document this. // VFALCO TODO Document this.
// NOTE What's the return value? // NOTE What's the return value?
int import (String sourceBackendParameters); virtual int import (String sourceBackendParameters) = 0;
private:
NodeObject::pointer retrieve (Backend* backend, uint256 const& hash);
void importVisitor (std::vector <NodeObject::pointer>& objects, NodeObject::pointer object);
static Backend* createBackend (String const& parameters);
static Array <BackendFactory*> s_factories;
private:
// Persistent key/value storage.
ScopedPointer <Backend> m_backend;
// Larger key/value storage, but not necessarily persistent.
ScopedPointer <Backend> m_fastBackend;
// VFALCO NOTE What are these things for? We need comments.
TaggedCache <uint256, NodeObject, UptimeTimerAdapter> m_cache;
KeyCache <uint256, UptimeTimerAdapter> m_negativeCache;
}; };
#endif #endif

View File

@@ -62,7 +62,7 @@ String NullBackendFactory::getName () const
return "none"; return "none";
} }
NodeStore::Backend* NullBackendFactory::createInstance (StringPairArray const& keyValues) NodeStore::Backend* NullBackendFactory::createInstance (size_t, StringPairArray const&)
{ {
return new NullBackendFactory::Backend; return new NullBackendFactory::Backend;
} }

View File

@@ -23,7 +23,7 @@ public:
static NullBackendFactory& getInstance (); static NullBackendFactory& getInstance ();
String getName () const; String getName () const;
NodeStore::Backend* createInstance (StringPairArray const& keyValues); NodeStore::Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues);
}; };
#endif #endif

View File

@@ -4,32 +4,65 @@
*/ */
//============================================================================== //==============================================================================
static const char* s_nodeStoreDBInit [] =
{
"PRAGMA synchronous=NORMAL;",
"PRAGMA journal_mode=WAL;",
"PRAGMA journal_size_limit=1582080;",
#if (ULONG_MAX > UINT_MAX) && !defined (NO_SQLITE_MMAP)
"PRAGMA mmap_size=171798691840;",
#endif
"BEGIN TRANSACTION;",
"CREATE TABLE CommittedObjects ( \
Hash CHARACTER(64) PRIMARY KEY, \
ObjType CHAR(1) NOT NULL, \
LedgerIndex BIGINT UNSIGNED, \
Object BLOB \
);",
"END TRANSACTION;"
};
static int s_nodeStoreDBCount = NUMBER (s_nodeStoreDBInit);
class SqliteBackendFactory::Backend : public NodeStore::Backend class SqliteBackendFactory::Backend : public NodeStore::Backend
{ {
public: public:
Backend(std::string const& path) : mName(path) Backend (size_t keyBytes, std::string const& path)
: m_keyBytes (keyBytes)
, m_name (path)
, m_db (new DatabaseCon(path, s_nodeStoreDBInit, s_nodeStoreDBCount))
{ {
mDb = new DatabaseCon(path, HashNodeDBInit, HashNodeDBCount); String s;
mDb->getDB()->executeSQL(boost::str(boost::format("PRAGMA cache_size=-%d;") %
(theConfig.getSize(siHashNodeDBCache) * 1024))); // VFALCO TODO Remove this dependency on theConfig
//
s << "PRAGMA cache_size=-" << String (theConfig.getSize(siHashNodeDBCache) * 1024);
m_db->getDB()->executeSQL (s.toStdString ().c_str ());
//m_db->getDB()->executeSQL (boost::str (boost::format ("PRAGMA cache_size=-%d;") %
// (theConfig.getSize(siHashNodeDBCache) * 1024)));
} }
Backend() ~Backend()
{ {
delete mDb; delete m_db;
} }
std::string getDataBaseName() std::string getDataBaseName()
{ {
return mName; return m_name;
} }
bool bulkStore(const std::vector< NodeObject::pointer >& objects) bool bulkStore (const std::vector< NodeObject::pointer >& objects)
{ {
ScopedLock sl(mDb->getDBLock()); ScopedLock sl(m_db->getDBLock());
static SqliteStatement pStB(mDb->getDB()->getSqliteDB(), "BEGIN TRANSACTION;"); static SqliteStatement pStB(m_db->getDB()->getSqliteDB(), "BEGIN TRANSACTION;");
static SqliteStatement pStE(mDb->getDB()->getSqliteDB(), "END TRANSACTION;"); static SqliteStatement pStE(m_db->getDB()->getSqliteDB(), "END TRANSACTION;");
static SqliteStatement pSt(mDb->getDB()->getSqliteDB(), static SqliteStatement pSt(m_db->getDB()->getSqliteDB(),
"INSERT OR IGNORE INTO CommittedObjects " "INSERT OR IGNORE INTO CommittedObjects "
"(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);"); "(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);");
@@ -55,8 +88,8 @@ public:
NodeObject::pointer ret; NodeObject::pointer ret;
{ {
ScopedLock sl(mDb->getDBLock()); ScopedLock sl(m_db->getDBLock());
static SqliteStatement pSt(mDb->getDB()->getSqliteDB(), static SqliteStatement pSt(m_db->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
pSt.bind(1, hash.GetHex()); pSt.bind(1, hash.GetHex());
@@ -74,7 +107,7 @@ public:
{ {
uint256 hash; uint256 hash;
static SqliteStatement pSt(mDb->getDB()->getSqliteDB(), static SqliteStatement pSt(m_db->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object,Hash FROM CommittedObjects;"); "SELECT ObjType,LedgerIndex,Object,Hash FROM CommittedObjects;");
while (pSt.isRow(pSt.step())) while (pSt.isRow(pSt.step()))
@@ -121,8 +154,9 @@ public:
} }
private: private:
std::string mName; size_t const m_keyBytes;
DatabaseCon* mDb; std::string const m_name;
ScopedPointer <DatabaseCon> m_db;
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -147,7 +181,7 @@ String SqliteBackendFactory::getName () const
return "Sqlite"; return "Sqlite";
} }
NodeStore::Backend* SqliteBackendFactory::createInstance (StringPairArray const& keyValues) NodeStore::Backend* SqliteBackendFactory::createInstance (size_t keyBytes, StringPairArray const& keyValues)
{ {
return new Backend (keyValues ["path"].toStdString ()); return new Backend (keyBytes, keyValues ["path"].toStdString ());
} }

View File

@@ -21,7 +21,7 @@ public:
static SqliteBackendFactory& getInstance (); static SqliteBackendFactory& getInstance ();
String getName () const; String getName () const;
NodeStore::Backend* createInstance (StringPairArray const& keyValues); NodeStore::Backend* createInstance (size_t keyBytes, StringPairArray const& keyValues);
}; };
#endif #endif

View File

@@ -102,9 +102,9 @@ namespace ripple
#include "node/ripple_NodeObject.h" #include "node/ripple_NodeObject.h"
#include "node/ripple_NodeStore.h" #include "node/ripple_NodeStore.h"
#include "node/ripple_LevelDBBackendFactory.h"
#include "node/ripple_HyperLevelDBBackendFactory.h" #include "node/ripple_HyperLevelDBBackendFactory.h"
#include "node/ripple_KeyvaDBBackendFactory.h" #include "node/ripple_KeyvaDBBackendFactory.h"
#include "node/ripple_LevelDBBackendFactory.h"
#include "node/ripple_MdbBackendFactory.h" #include "node/ripple_MdbBackendFactory.h"
#include "node/ripple_NullBackendFactory.h" #include "node/ripple_NullBackendFactory.h"
#include "node/ripple_SqliteBackendFactory.h" #include "node/ripple_SqliteBackendFactory.h"
@@ -245,14 +245,14 @@ static const uint64 tenTo17m1 = tenTo17 - 1;
#include "basics/ripple_RPCServerHandler.cpp" #include "basics/ripple_RPCServerHandler.cpp"
#include "node/ripple_NodeObject.cpp" #include "node/ripple_NodeObject.cpp"
#include "node/ripple_NodeStore.cpp" #include "node/ripple_NodeStore.cpp"
#include "node/ripple_LevelDBBackendFactory.cpp"
#include "node/ripple_HyperLevelDBBackendFactory.cpp" #include "node/ripple_HyperLevelDBBackendFactory.cpp"
#include "node/ripple_MdbBackendFactory.cpp"
#include "node/ripple_NullBackendFactory.cpp"
#include "node/ripple_SqliteBackendFactory.cpp"
#include "node/ripple_KeyvaDB.h" // private #include "node/ripple_KeyvaDB.h" // private
#include "node/ripple_KeyvaDB.cpp" #include "node/ripple_KeyvaDB.cpp"
#include "node/ripple_KeyvaDBBackendFactory.cpp" #include "node/ripple_KeyvaDBBackendFactory.cpp"
#include "node/ripple_LevelDBBackendFactory.cpp"
#include "node/ripple_NullBackendFactory.cpp"
#include "node/ripple_MdbBackendFactory.cpp"
#include "node/ripple_SqliteBackendFactory.cpp"
#include "ledger/Ledger.cpp" #include "ledger/Ledger.cpp"
#include "src/cpp/ripple/ripple_SHAMapDelta.cpp" #include "src/cpp/ripple/ripple_SHAMapDelta.cpp"

View File

@@ -115,7 +115,22 @@ public:
} }
bool del (const key_type& key, bool valid); bool del (const key_type& key, bool valid);
/** Replace aliased objects with originals.
Due to concurrency it is possible for two separate objects with
the same content and referring to the same unique "thing" to exist.
This routine eliminates the duplicate and performs a replacement
on the callers shared pointer if needed.
@param key The key corresponding to the object
@param data A shared pointer to the data corresponding to the object.
@param replace `true` if `data` is the up to date version of the object.
@return `true` if the operation was successful.
*/
bool canonicalize (const key_type& key, boost::shared_ptr<c_Data>& data, bool replace = false); bool canonicalize (const key_type& key, boost::shared_ptr<c_Data>& data, bool replace = false);
bool store (const key_type& key, const c_Data& data); bool store (const key_type& key, const c_Data& data);
boost::shared_ptr<c_Data> fetch (const key_type& key); boost::shared_ptr<c_Data> fetch (const key_type& key);
bool retrieve (const key_type& key, c_Data& data); bool retrieve (const key_type& key, c_Data& data);

View File

@@ -44,6 +44,9 @@ void NetworkOPs::processNetTimer ()
{ {
ScopedLock sl (getApp().getMasterLock ()); ScopedLock sl (getApp().getMasterLock ());
Application& app (getApp ());
ILoadManager& mgr (app.getLoadManager ());
getApp().getLoadManager ().resetDeadlockDetector (); getApp().getLoadManager ().resetDeadlockDetector ();
std::size_t const numPeers = getApp().getPeers ().getPeerVector ().size (); std::size_t const numPeers = getApp().getPeers ().getPeerVector ().size ();

View File

@@ -46,10 +46,11 @@ public:
, mNetOps (&mLedgerMaster) , mNetOps (&mLedgerMaster)
, m_rpcServerHandler (mNetOps) , m_rpcServerHandler (mNetOps)
, mTempNodeCache ("NodeCache", 16384, 90) , mTempNodeCache ("NodeCache", 16384, 90)
, m_nodeStore ( , m_nodeStore (NodeStore::New (
theConfig.NODE_DB, theConfig.NODE_DB,
theConfig.FASTNODE_DB, theConfig.FASTNODE_DB,
16384, 300) 16384,
300))
, mSLECache ("LedgerEntryCache", 4096, 120) , mSLECache ("LedgerEntryCache", 4096, 120)
, mSNTPClient (mAuxService) , mSNTPClient (mAuxService)
, mJobQueue (mIOService) , mJobQueue (mIOService)
@@ -70,11 +71,6 @@ public:
, mTxnDB (NULL) , mTxnDB (NULL)
, mLedgerDB (NULL) , mLedgerDB (NULL)
, mWalletDB (NULL) // VFALCO NOTE are all these 'NULL' ctor params necessary? , mWalletDB (NULL) // VFALCO NOTE are all these 'NULL' ctor params necessary?
, mNetNodeDB (NULL)
, mPathFindDB (NULL)
, mHashNodeDB (NULL)
, mHashNodeLDB (NULL)
, mEphemeralLDB (NULL)
, mPeerDoor (NULL) , mPeerDoor (NULL)
, mRPCDoor (NULL) , mRPCDoor (NULL)
, mWSPublicDoor (NULL) , mWSPublicDoor (NULL)
@@ -92,13 +88,6 @@ public:
delete mTxnDB; delete mTxnDB;
delete mLedgerDB; delete mLedgerDB;
delete mWalletDB; delete mWalletDB;
delete mHashNodeDB;
delete mNetNodeDB;
delete mPathFindDB;
delete mHashNodeLDB;
if (mEphemeralLDB != nullptr)
delete mEphemeralLDB;
} }
LocalCredentials& getLocalCredentials () LocalCredentials& getLocalCredentials ()
@@ -138,7 +127,7 @@ public:
NodeStore& getNodeStore () NodeStore& getNodeStore ()
{ {
return m_nodeStore; return *m_nodeStore;
} }
JobQueue& getJobQueue () JobQueue& getJobQueue ()
@@ -247,27 +236,6 @@ public:
{ {
return mWalletDB; return mWalletDB;
} }
DatabaseCon* getNetNodeDB ()
{
return mNetNodeDB;
}
DatabaseCon* getPathFindDB ()
{
return mPathFindDB;
}
DatabaseCon* getHashNodeDB ()
{
return mHashNodeDB;
}
leveldb::DB* getHashNodeLDB ()
{
return mHashNodeLDB;
}
leveldb::DB* getEphemeralLDB ()
{
return mEphemeralLDB;
}
bool isShutdown () bool isShutdown ()
{ {
@@ -302,7 +270,7 @@ private:
NetworkOPs mNetOps; NetworkOPs mNetOps;
RPCServerHandler m_rpcServerHandler; RPCServerHandler m_rpcServerHandler;
NodeCache mTempNodeCache; NodeCache mTempNodeCache;
NodeStore m_nodeStore; ScopedPointer <NodeStore> m_nodeStore;
SLECache mSLECache; SLECache mSLECache;
SNTPClient mSNTPClient; SNTPClient mSNTPClient;
JobQueue mJobQueue; JobQueue mJobQueue;
@@ -326,13 +294,6 @@ private:
DatabaseCon* mTxnDB; DatabaseCon* mTxnDB;
DatabaseCon* mLedgerDB; DatabaseCon* mLedgerDB;
DatabaseCon* mWalletDB; DatabaseCon* mWalletDB;
DatabaseCon* mNetNodeDB;
DatabaseCon* mPathFindDB;
DatabaseCon* mHashNodeDB;
// VFALCO TODO Wrap this in an interface
leveldb::DB* mHashNodeLDB;
leveldb::DB* mEphemeralLDB;
ScopedPointer <PeerDoor> mPeerDoor; ScopedPointer <PeerDoor> mPeerDoor;
ScopedPointer <RPCDoor> mRPCDoor; ScopedPointer <RPCDoor> mRPCDoor;
@@ -353,19 +314,11 @@ void ApplicationImp::stop ()
StopSustain (); StopSustain ();
mShutdown = true; mShutdown = true;
mIOService.stop (); mIOService.stop ();
// VFALCO TODO We shouldn't have to explicitly call this function. m_nodeStore = nullptr;
// The NodeStore destructor should take care of it.
m_nodeStore.waitWrite ();
mValidations->flush (); mValidations->flush ();
mAuxService.stop (); mAuxService.stop ();
mJobQueue.shutdown (); mJobQueue.shutdown ();
delete mHashNodeLDB;
mHashNodeLDB = NULL;
delete mEphemeralLDB;
mEphemeralLDB = NULL;
WriteLog (lsINFO, Application) << "Stopped: " << mIOService.stopped (); WriteLog (lsINFO, Application) << "Stopped: " << mIOService.stopped ();
mShutdown = false; mShutdown = false;
} }
@@ -445,16 +398,11 @@ void ApplicationImp::setup ()
boost::thread t1 (BIND_TYPE (&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount)); boost::thread t1 (BIND_TYPE (&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount));
boost::thread t2 (BIND_TYPE (&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount)); boost::thread t2 (BIND_TYPE (&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount));
boost::thread t3 (BIND_TYPE (&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount)); boost::thread t3 (BIND_TYPE (&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount));
boost::thread t4 (BIND_TYPE (&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount));
t1.join (); t1.join ();
t2.join (); t2.join ();
t3.join (); t3.join ();
boost::thread t4 (BIND_TYPE (&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount));
boost::thread t6 (BIND_TYPE (&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount));
boost::thread t7 (BIND_TYPE (&InitDB, &mPathFindDB, "pathfind.db", PathFindDBInit, PathFindDBCount));
t4.join (); t4.join ();
t6.join ();
t7.join ();
leveldb::Options options; leveldb::Options options;
options.create_if_missing = true; options.create_if_missing = true;
@@ -515,7 +463,7 @@ void ApplicationImp::setup ()
getUNL ().nodeBootstrap (); getUNL ().nodeBootstrap ();
mValidations->tune (theConfig.getSize (siValidationsSize), theConfig.getSize (siValidationsAge)); mValidations->tune (theConfig.getSize (siValidationsSize), theConfig.getSize (siValidationsAge));
m_nodeStore.tune (theConfig.getSize (siNodeCacheSize), theConfig.getSize (siNodeCacheAge)); m_nodeStore->tune (theConfig.getSize (siNodeCacheSize), theConfig.getSize (siNodeCacheAge));
mLedgerMaster.tune (theConfig.getSize (siLedgerSize), theConfig.getSize (siLedgerAge)); mLedgerMaster.tune (theConfig.getSize (siLedgerSize), theConfig.getSize (siLedgerAge));
mSLECache.setTargetSize (theConfig.getSize (siSLECacheSize)); mSLECache.setTargetSize (theConfig.getSize (siSLECacheSize));
mSLECache.setTargetAge (theConfig.getSize (siSLECacheAge)); mSLECache.setTargetAge (theConfig.getSize (siSLECacheAge));
@@ -697,7 +645,7 @@ void ApplicationImp::doSweep(Job& j)
// //
mMasterTransaction.sweep (); mMasterTransaction.sweep ();
m_nodeStore.sweep (); m_nodeStore->sweep ();
mLedgerMaster.sweep (); mLedgerMaster.sweep ();
mTempNodeCache.sweep (); mTempNodeCache.sweep ();
mValidations->sweep (); mValidations->sweep ();

View File

@@ -89,15 +89,9 @@ public:
It looks like this is used to store the unique node list. It looks like this is used to store the unique node list.
*/ */
// VFALCO TODO Rename, document this // VFALCO TODO Rename, document this
// NOTE This will be replaced by class Validators
//
virtual DatabaseCon* getWalletDB () = 0; virtual DatabaseCon* getWalletDB () = 0;
// VFALCO NOTE It looks like this isn't used...
//virtual DatabaseCon* getNetNodeDB () = 0;
// VFALCO NOTE It looks like this isn't used...
//virtual DatabaseCon* getPathFindDB () = 0;
virtual DatabaseCon* getHashNodeDB () = 0;
virtual leveldb::DB* getHashNodeLDB () = 0;
virtual leveldb::DB* getEphemeralLDB () = 0;
virtual bool getSystemTimeOffset (int& offset) = 0; virtual bool getSystemTimeOffset (int& offset) = 0;
virtual bool isShutdown () = 0; virtual bool isShutdown () = 0;

View File

@@ -257,15 +257,16 @@ int rippleMain (int argc, char** argv)
p.add ("parameters", -1); p.add ("parameters", -1);
// These must be added before the Application object is created // These must be added before the Application object is created
NodeStore::addBackendFactory (SqliteBackendFactory::getInstance ());
NodeStore::addBackendFactory (LevelDBBackendFactory::getInstance ());
NodeStore::addBackendFactory (KeyvaDBBackendFactory::getInstance ());
#if RIPPLE_HYPERLEVELDB_AVAILABLE #if RIPPLE_HYPERLEVELDB_AVAILABLE
NodeStore::addBackendFactory (HyperLevelDBBackendFactory::getInstance ()); NodeStore::addBackendFactory (HyperLevelDBBackendFactory::getInstance ());
#endif #endif
NodeStore::addBackendFactory (KeyvaDBBackendFactory::getInstance ());
NodeStore::addBackendFactory (LevelDBBackendFactory::getInstance ());
#if RIPPLE_MDB_AVAILABLE #if RIPPLE_MDB_AVAILABLE
NodeStore::addBackendFactory (MdbBackendFactory::getInstance ()); NodeStore::addBackendFactory (MdbBackendFactory::getInstance ());
#endif #endif
NodeStore::addBackendFactory (NullBackendFactory::getInstance ());
NodeStore::addBackendFactory (SqliteBackendFactory::getInstance ());
if (! RandomNumbers::getInstance ().initialize ()) if (! RandomNumbers::getInstance ().initialize ())
{ {