Files
rippled/modules/ripple_app/node/ripple_MdbBackendFactory.cpp
2013-07-21 12:56:27 -07:00

280 lines
6.9 KiB
C++

//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#if RIPPLE_MDB_AVAILABLE
class MdbBackendFactory::Backend
: public NodeStore::Backend
, public NodeStore::BatchWriter::Callback
, LeakChecked <MdbBackendFactory::Backend>
{
public:
typedef NodeStore::Batch Batch;
typedef NodeStore::EncodedBlob EncodedBlob;
typedef NodeStore::DecodedBlob DecodedBlob;
explicit Backend (size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler)
: m_keyBytes (keyBytes)
, m_scheduler (scheduler)
, m_batch (*this, scheduler)
, m_env (nullptr)
{
String path (keyValues ["path"]);
if (path.isEmpty ())
Throw (std::runtime_error ("Missing path in MDB backend"));
m_basePath = path.toStdString();
// Regarding the path supplied to mdb_env_open:
// This directory must already exist and be writable.
//
File dir (File::getCurrentWorkingDirectory().getChildFile (path));
Result result = dir.createDirectory ();
if (result.wasOk ())
{
int error = mdb_env_create (&m_env);
// Should use the size of the file plus the free space on the disk
if (error == 0)
error = mdb_env_set_mapsize (m_env, 512L * 1024L * 1024L * 1024L);
if (error == 0)
error = mdb_env_open (
m_env,
m_basePath.c_str (),
MDB_NOTLS,
0664);
MDB_txn* txn;
if (error == 0)
error = mdb_txn_begin (m_env, NULL, 0, &txn);
if (error == 0)
error = mdb_dbi_open (txn, NULL, 0, &m_dbi);
if (error == 0)
error = mdb_txn_commit (txn);
if (error != 0)
{
String s;
s << "Error #" << error << " creating mdb environment";
Throw (std::runtime_error (s.toStdString ()));
}
}
else
{
String s;
s << "MDB Backend failed to create directory, " << result.getErrorMessage ();
Throw (std::runtime_error (s.toStdString().c_str()));
}
}
~Backend ()
{
if (m_env != nullptr)
{
mdb_dbi_close (m_env, m_dbi);
mdb_env_close (m_env);
}
}
std::string getName()
{
return m_basePath;
}
//--------------------------------------------------------------------------
template <class T>
unsigned char* mdb_cast (T* p)
{
return const_cast <unsigned char*> (static_cast <unsigned char const*> (p));
}
Status fetch (void const* key, NodeObject::Ptr* pObject)
{
pObject->reset ();
Status status (ok);
MDB_txn* txn = nullptr;
int error = 0;
error = mdb_txn_begin (m_env, NULL, MDB_RDONLY, &txn);
if (error == 0)
{
MDB_val dbkey;
MDB_val data;
dbkey.mv_size = m_keyBytes;
dbkey.mv_data = mdb_cast (key);
error = mdb_get (txn, m_dbi, &dbkey, &data);
if (error == 0)
{
DecodedBlob decoded (key, data.mv_data, data.mv_size);
if (decoded.wasOk ())
{
*pObject = decoded.createObject ();
}
else
{
status = dataCorrupt;
}
}
else if (error == MDB_NOTFOUND)
{
status = notFound;
}
else
{
status = unknown;
WriteLog (lsWARNING, NodeObject) << "MDB txn failed, code=" << error;
}
mdb_txn_abort (txn);
}
else
{
status = unknown;
WriteLog (lsWARNING, NodeObject) << "MDB txn failed, code=" << error;
}
return status;
}
void store (NodeObject::ref object)
{
m_batch.store (object);
}
void storeBatch (Batch const& batch)
{
MDB_txn* txn = nullptr;
int error = 0;
error = mdb_txn_begin (m_env, NULL, 0, &txn);
if (error == 0)
{
EncodedBlob::Pool::ScopedItem item (m_blobPool);
BOOST_FOREACH (NodeObject::Ptr const& object, batch)
{
EncodedBlob& encoded (item.getObject ());
encoded.prepare (object);
MDB_val key;
key.mv_size = m_keyBytes;
key.mv_data = mdb_cast (encoded.getKey ());
MDB_val data;
data.mv_size = encoded.getSize ();
data.mv_data = mdb_cast (encoded.getData ());
error = mdb_put (txn, m_dbi, &key, &data, 0);
if (error != 0)
{
WriteLog (lsWARNING, NodeObject) << "mdb_put failed, error=" << error;
break;
}
}
if (error == 0)
{
error = mdb_txn_commit(txn);
if (error != 0)
{
WriteLog (lsWARNING, NodeObject) << "mdb_txn_commit failed, error=" << error;
}
}
else
{
mdb_txn_abort (txn);
}
}
else
{
WriteLog (lsWARNING, NodeObject) << "mdb_txn_begin failed, error=" << error;
}
}
void visitAll (VisitCallback& callback)
{
// VFALCO TODO Implement this!
bassertfalse;
}
int getWriteLoad ()
{
return m_batch.getWriteLoad ();
}
//--------------------------------------------------------------------------
void writeBatch (Batch const& batch)
{
storeBatch (batch);
}
private:
size_t const m_keyBytes;
NodeStore::Scheduler& m_scheduler;
NodeStore::BatchWriter m_batch;
NodeStore::EncodedBlob::Pool m_blobPool;
std::string m_basePath;
MDB_env* m_env;
MDB_dbi m_dbi;
};
//------------------------------------------------------------------------------
MdbBackendFactory::MdbBackendFactory ()
{
}
MdbBackendFactory::~MdbBackendFactory ()
{
}
MdbBackendFactory& MdbBackendFactory::getInstance ()
{
static MdbBackendFactory instance;
return instance;
}
String MdbBackendFactory::getName () const
{
return "mdb";
}
NodeStore::Backend* MdbBackendFactory::createInstance (
size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler)
{
return new MdbBackendFactory::Backend (keyBytes, keyValues, scheduler);
}
#endif