mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-01 00:15:51 +00:00
Add negative caching for HashedObject class. This massively reduces
contention for the database lock under high network ledger fetch load.
This commit is contained in:
@@ -33,6 +33,7 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index,
|
||||
}
|
||||
assert(hash == Serializer::getSHA512Half(data));
|
||||
|
||||
mNegativeCache.del(hash);
|
||||
HashedObject::pointer object = boost::make_shared<HashedObject>(type, index, data, hash);
|
||||
if (!mCache.canonicalize(hash, object))
|
||||
{
|
||||
@@ -115,6 +116,7 @@ void HashedObjectStore::bulkWrite()
|
||||
|
||||
HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
{
|
||||
|
||||
HashedObject::pointer obj;
|
||||
{
|
||||
obj = mCache.fetch(hash);
|
||||
@@ -125,6 +127,9 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
}
|
||||
}
|
||||
|
||||
if (mNegativeCache.isPresent(hash))
|
||||
return HashedObject::pointer();
|
||||
|
||||
if (!theApp || !theApp->getHashNodeDB())
|
||||
return HashedObject::pointer();
|
||||
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
|
||||
@@ -139,12 +144,17 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
{
|
||||
// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db";
|
||||
mNegativeCache.add(hash);
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
std::string type;
|
||||
db->getStr("ObjType", type);
|
||||
if (type.size() == 0) return HashedObject::pointer();
|
||||
if (type.size() == 0)
|
||||
{
|
||||
mNegativeCache.add(hash);
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
uint32 index = db->getBigInt("LedgerIndex");
|
||||
|
||||
@@ -164,6 +174,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
case 'N': htype = hotTRANSACTION_NODE; break;
|
||||
default:
|
||||
cLog(lsERROR) << "Invalid hashed object";
|
||||
mNegativeCache.add(hash);
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "uint256.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "KeyCache.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(HashedObject);
|
||||
@@ -45,7 +46,8 @@ public:
|
||||
class HashedObjectStore
|
||||
{
|
||||
protected:
|
||||
TaggedCache<uint256, HashedObject> mCache;
|
||||
TaggedCache<uint256, HashedObject> mCache;
|
||||
KeyCache<uint256> mNegativeCache;
|
||||
|
||||
boost::mutex mWriteMutex;
|
||||
boost::condition_variable mWriteCondition;
|
||||
@@ -65,7 +67,7 @@ public:
|
||||
|
||||
void bulkWrite();
|
||||
void waitWrite();
|
||||
void sweep() { mCache.sweep(); }
|
||||
void sweep() { mCache.sweep(); mNegativeCache.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
109
src/cpp/ripple/KeyCache.h
Normal file
109
src/cpp/ripple/KeyCache.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#ifndef KEY_CACHE__H
|
||||
#define KEY_CACHE__H
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
template <typename c_Key> class KeyCache
|
||||
{ // Maintains a cache of keys with no associated data
|
||||
public:
|
||||
typedef c_Key key_type;
|
||||
typedef boost::unordered_map<key_type, time_t> map_type;
|
||||
typedef typename map_type::iterator map_iterator;
|
||||
|
||||
protected:
|
||||
boost::mutex mNCLock;
|
||||
map_type mCache;
|
||||
int mTargetSize, mTargetAge;
|
||||
|
||||
uint64_t mHits, mMisses;
|
||||
|
||||
public:
|
||||
|
||||
KeyCache(int size = 0, int age = 120) : mTargetSize(size), mTargetAge(age), mHits(0), mMisses(0)
|
||||
{
|
||||
assert((mTargetSize >= 0) && (mTargetAge > 2));
|
||||
}
|
||||
|
||||
void getStats(int& size, uint64_t& hits, uint64_t& misses)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
size = mCache.size();
|
||||
hits = mHits;
|
||||
misses = mMisses;
|
||||
}
|
||||
|
||||
bool isPresent(const key_type& key)
|
||||
{ // Check if an entry is cached, refresh it if so
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it == mCache.end())
|
||||
{
|
||||
++mMisses;
|
||||
return false;
|
||||
}
|
||||
it->second = time(NULL);
|
||||
++mHits;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool del(const key_type& key)
|
||||
{ // Remove an entry from the cache, return false if not-present
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it == mCache.end())
|
||||
return false;
|
||||
|
||||
mCache.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add(const key_type& key)
|
||||
{ // Add an entry to the cache, return true if it is new
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
map_iterator it = mCache.find(key);
|
||||
if (it != mCache.end())
|
||||
{
|
||||
it->second = time(NULL);
|
||||
return false;
|
||||
}
|
||||
mCache.insert(std::make_pair(key, time(NULL)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void sweep()
|
||||
{ // Remove stale entries from the cache
|
||||
time_t now = time(NULL);
|
||||
boost::mutex::scoped_lock sl(mNCLock);
|
||||
|
||||
time_t target;
|
||||
if ((mTargetSize == 0) || (mCache.size() <= mTargetSize))
|
||||
target = now - mTargetAge;
|
||||
else
|
||||
{
|
||||
target = now - (mTargetAge * mTargetSize / mCache.size());
|
||||
if (target > (now - 2))
|
||||
target = now - 2;
|
||||
}
|
||||
|
||||
map_iterator it = mCache.begin();
|
||||
while (it != mCache.end())
|
||||
{
|
||||
if (it->second > now)
|
||||
{
|
||||
it->second = now;
|
||||
++it;
|
||||
}
|
||||
else if (it->second < target)
|
||||
it = mCache.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user