mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-02 08:17:13 +00:00
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
This commit is contained in:
@@ -953,6 +953,21 @@
|
||||
#
|
||||
# Optional keys for NuDB and RocksDB:
|
||||
#
|
||||
# cache_size Size of cache for database records. Default is 16384.
|
||||
# Setting this value to 0 will use the default value.
|
||||
#
|
||||
# cache_age Length of time in minutes to keep database records
|
||||
# cached. Default is 5 minutes. Setting this value to
|
||||
# 0 will use the default value.
|
||||
#
|
||||
# Note: if cache_size or cache_age is not specified,
|
||||
# default values will be used for the unspecified
|
||||
# parameter.
|
||||
#
|
||||
# Note: the cache will not be created if online_delete
|
||||
# is specified, because the rotating NodeStore does
|
||||
# not use this cache).
|
||||
#
|
||||
# fast_load Boolean. If set, load the last persisted ledger
|
||||
# from disk upon process start before syncing to
|
||||
# the network. This is likely to improve performance
|
||||
|
||||
@@ -131,6 +131,10 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback);
|
||||
|
||||
/** Remove expired entries from the positive and negative caches. */
|
||||
virtual void
|
||||
sweep() = 0;
|
||||
|
||||
/** Gather statistics pertaining to read and write activities.
|
||||
*
|
||||
* @param obj Json object reference into which to place counters.
|
||||
|
||||
@@ -22,6 +22,32 @@ public:
|
||||
beast::Journal j)
|
||||
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
|
||||
{
|
||||
std::optional<int> cacheSize, cacheAge;
|
||||
|
||||
if (config.exists("cache_size"))
|
||||
{
|
||||
cacheSize = get<int>(config, "cache_size");
|
||||
if (cacheSize.value() < 0)
|
||||
Throw<std::runtime_error>("Specified negative value for cache_size");
|
||||
}
|
||||
|
||||
if (config.exists("cache_age"))
|
||||
{
|
||||
cacheAge = get<int>(config, "cache_age");
|
||||
if (cacheAge.value() < 0)
|
||||
Throw<std::runtime_error>("Specified negative value for cache_age");
|
||||
}
|
||||
|
||||
if (cacheSize.has_value() || cacheAge.has_value())
|
||||
{
|
||||
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
|
||||
"DatabaseNodeImp",
|
||||
cacheSize.value_or(0),
|
||||
std::chrono::minutes(cacheAge.value_or(0)),
|
||||
stopwatch(),
|
||||
j);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
backend_,
|
||||
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
@@ -73,7 +99,13 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback) override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
// Cache for database objects. This cache is not always initialized. Check
|
||||
// for null before using.
|
||||
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
|
||||
// Persistent key/value storage
|
||||
std::shared_ptr<Backend> backend_;
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ public:
|
||||
void
|
||||
sync() override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Backend> writableBackend_;
|
||||
std::shared_ptr<Backend> archiveBackend_;
|
||||
|
||||
@@ -24,6 +24,13 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st
|
||||
|
||||
auto obj = NodeObject::createObject(type, std::move(data), hash);
|
||||
backend_->store(obj);
|
||||
if (cache_)
|
||||
{
|
||||
// After the store, replace a negative cache entry if there is one
|
||||
cache_->canonicalize(hash, obj, [](std::shared_ptr<NodeObject> const& n) {
|
||||
return n->getType() == NodeObjectType::Dummy;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -32,9 +39,25 @@ DatabaseNodeImp::asyncFetch(
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback)
|
||||
{
|
||||
if (cache_)
|
||||
{
|
||||
std::shared_ptr<NodeObject> const obj = cache_->fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
callback(obj->getType() == NodeObjectType::Dummy ? nullptr : obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Database::asyncFetch(hash, ledgerSeq, std::move(callback));
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseNodeImp::sweep()
|
||||
{
|
||||
if (cache_)
|
||||
cache_->sweep();
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
DatabaseNodeImp::fetchNodeObject(
|
||||
uint256 const& hash,
|
||||
@@ -42,32 +65,58 @@ DatabaseNodeImp::fetchNodeObject(
|
||||
FetchReport& fetchReport,
|
||||
bool duplicate)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nodeObject = nullptr;
|
||||
Status status = Status::Ok;
|
||||
std::shared_ptr<NodeObject> nodeObject = cache_ ? cache_->fetch(hash) : nullptr;
|
||||
if (!nodeObject)
|
||||
{
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not "
|
||||
<< (cache_ ? "cached" : "found");
|
||||
|
||||
try
|
||||
{
|
||||
status = backend_->fetch(hash, &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash
|
||||
<< ": Exception fetching from backend: " << e.what();
|
||||
rethrow();
|
||||
}
|
||||
Status status = Status::Ok;
|
||||
try
|
||||
{
|
||||
status = backend_->fetch(hash, &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash
|
||||
<< ": Exception fetching from backend: " << e.what();
|
||||
rethrow();
|
||||
}
|
||||
|
||||
switch (status)
|
||||
switch (status)
|
||||
{
|
||||
case Status::Ok:
|
||||
if (cache_)
|
||||
{
|
||||
if (nodeObject)
|
||||
{
|
||||
cache_->canonicalizeReplaceClient(hash, nodeObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto notFound = NodeObject::createObject(NodeObjectType::Dummy, {}, hash);
|
||||
cache_->canonicalizeReplaceClient(hash, notFound);
|
||||
if (notFound->getType() != NodeObjectType::Dummy)
|
||||
nodeObject = notFound;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Status::NotFound:
|
||||
break;
|
||||
case Status::DataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result "
|
||||
<< static_cast<int>(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
case Status::Ok:
|
||||
case Status::NotFound:
|
||||
break;
|
||||
case Status::DataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result "
|
||||
<< static_cast<int>(status);
|
||||
break;
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache";
|
||||
if (nodeObject->getType() == NodeObjectType::Dummy)
|
||||
nodeObject.reset();
|
||||
}
|
||||
|
||||
if (nodeObject)
|
||||
|
||||
@@ -113,6 +113,12 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash
|
||||
storeStats(1, nObj->getData().size());
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseRotatingImp::sweep()
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
DatabaseRotatingImp::fetchNodeObject(
|
||||
uint256 const& hash,
|
||||
|
||||
@@ -520,6 +520,25 @@ public:
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Create NodeStore with two backends to allow online deletion of data.
|
||||
// Normally, SHAMapStoreImp handles all these details.
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values.
|
||||
if (!nscfg.exists("cache_size"))
|
||||
{
|
||||
nscfg.set(
|
||||
"cache_size",
|
||||
std::to_string(
|
||||
env.app().config().getValueFor(SizedItem::TreeCacheSize, std::nullopt)));
|
||||
}
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
{
|
||||
nscfg.set(
|
||||
"cache_age",
|
||||
std::to_string(
|
||||
env.app().config().getValueFor(SizedItem::TreeCacheAge, std::nullopt)));
|
||||
}
|
||||
|
||||
NodeStoreScheduler scheduler(env.app().getJobQueue());
|
||||
|
||||
std::string const writableDb = "write";
|
||||
@@ -528,7 +547,6 @@ public:
|
||||
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
|
||||
|
||||
static constexpr int kReadThreads = 4;
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
|
||||
scheduler,
|
||||
kReadThreads,
|
||||
|
||||
@@ -998,6 +998,10 @@ public:
|
||||
JLOG(journal_.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
|
||||
<< "; size after: " << masterTxCache.size();
|
||||
}
|
||||
{
|
||||
// Sweep NodeStore database cache(s), if enabled.
|
||||
getNodeStore().sweep();
|
||||
}
|
||||
{
|
||||
std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();
|
||||
|
||||
|
||||
@@ -165,6 +165,22 @@ std::unique_ptr<NodeStore::Database>
|
||||
SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
{
|
||||
auto nscfg = app_.config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values.
|
||||
if (!nscfg.exists("cache_size"))
|
||||
{
|
||||
nscfg.set(
|
||||
"cache_size",
|
||||
std::to_string(app_.config().getValueFor(SizedItem::TreeCacheSize, std::nullopt)));
|
||||
}
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
{
|
||||
nscfg.set(
|
||||
"cache_age",
|
||||
std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt)));
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Database> db;
|
||||
|
||||
if (deleteInterval_ != 0u)
|
||||
@@ -254,6 +270,8 @@ SHAMapStoreImp::run()
|
||||
LedgerIndex lastRotated = stateDb_.getState().lastRotated;
|
||||
netOPs_ = &app_.getOPs();
|
||||
ledgerMaster_ = &app_.getLedgerMaster();
|
||||
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
|
||||
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
|
||||
|
||||
if (advisoryDelete_)
|
||||
canDelete_ = stateDb_.getCanDelete();
|
||||
@@ -542,16 +560,16 @@ SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
|
||||
// Also clear the FullBelowCache so its generation counter is bumped.
|
||||
// This prevents stale "full below" markers from persisting across
|
||||
// backend rotation/online deletion and interfering with SHAMap sync.
|
||||
app_.getNodeFamily().getFullBelowCache()->clear();
|
||||
fullBelowCache_->clear();
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapStoreImp::freshenCaches()
|
||||
{
|
||||
if (freshenCache(*app_.getNodeFamily().getTreeNodeCache()))
|
||||
if (freshenCache(*treeNodeCache_))
|
||||
return;
|
||||
if (freshenCache(app_.getMasterTransaction().getCache()))
|
||||
return;
|
||||
|
||||
freshenCache(app_.getMasterTransaction().getCache());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
#include <xrpl/server/State.h>
|
||||
#include <xrpl/shamap/FullBelowCache.h>
|
||||
#include <xrpl/shamap/TreeNodeCache.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@@ -93,6 +95,8 @@ private:
|
||||
// as of run() or before
|
||||
NetworkOPs* netOPs_ = nullptr;
|
||||
LedgerMaster* ledgerMaster_ = nullptr;
|
||||
FullBelowCache* fullBelowCache_ = nullptr;
|
||||
TreeNodeCache* treeNodeCache_ = nullptr;
|
||||
|
||||
static constexpr auto kNodeStoreName = "NodeStore";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user