Add Shard Family

This commit is contained in:
Miguel Portilla
2020-06-02 16:52:13 -04:00
committed by manojsdoshi
parent 91e857874f
commit 86e8f2e232
31 changed files with 742 additions and 339 deletions

View File

@@ -637,12 +637,14 @@ target_sources (rippled PRIVATE
main sources:
subdir: shamap
#]===============================]
src/ripple/shamap/impl/NodeFamily.cpp
src/ripple/shamap/impl/SHAMap.cpp
src/ripple/shamap/impl/SHAMapDelta.cpp
src/ripple/shamap/impl/SHAMapItem.cpp
src/ripple/shamap/impl/SHAMapNodeID.cpp
src/ripple/shamap/impl/SHAMapSync.cpp
src/ripple/shamap/impl/SHAMapTreeNode.cpp
src/ripple/shamap/impl/ShardFamily.cpp
#[===============================[
test sources:
subdir: app

View File

@@ -300,7 +300,7 @@ RCLConsensus::Adaptor::onClose(
auto initialLedger = app_.openLedger().current();
auto initialSet =
std::make_shared<SHAMap>(SHAMapType::TRANSACTION, app_.family());
std::make_shared<SHAMap>(SHAMapType::TRANSACTION, app_.getNodeFamily());
initialSet->setUnbacked();
// Build SHAMap containing all transactions in our open ledger

View File

@@ -248,7 +248,7 @@ Ledger::Ledger(
{
info_.hash = calculateLedgerHash(info_);
if (acquire)
family.missing_node(info_.hash, info_.seq);
family.missingNode(info_.hash, info_.seq);
}
}
@@ -1077,7 +1077,7 @@ loadLedgerHelper(std::string const& sqlSuffix, Application& app, bool acquire)
loaded,
acquire,
app.config(),
app.family(),
app.getNodeFamily(),
app.journal("Ledger"));
if (!loaded)

View File

@@ -98,7 +98,7 @@ InboundLedger::init(ScopedLockType& collectionLock)
ScopedLockType sl(mLock);
collectionLock.unlock();
tryDB(app_.family().db());
tryDB(app_.getNodeFamily().db());
if (mFailed)
return;
@@ -107,7 +107,7 @@ InboundLedger::init(ScopedLockType& collectionLock)
auto shardStore = app_.getShardStore();
if (mReason == Reason::SHARD)
{
if (!shardStore || !app_.shardFamily())
if (!shardStore)
{
JLOG(m_journal.error())
<< "Acquiring shard with no shard store available";
@@ -120,7 +120,7 @@ InboundLedger::init(ScopedLockType& collectionLock)
mHaveState = false;
mLedger.reset();
tryDB(app_.shardFamily()->db());
tryDB(app_.getShardFamily()->db());
if (mFailed)
return;
}
@@ -203,9 +203,9 @@ InboundLedger::checkLocal()
if (mLedger)
tryDB(mLedger->stateMap().family().db());
else if (mReason == Reason::SHARD)
tryDB(app_.shardFamily()->db());
tryDB(app_.getShardFamily()->db());
else
tryDB(app_.family().db());
tryDB(app_.getNodeFamily().db());
if (mFailed || mComplete)
{
done();
@@ -306,8 +306,8 @@ InboundLedger::tryDB(NodeStore::Database& srcDB)
mLedger = std::make_shared<Ledger>(
deserializePrefixedHeader(makeSlice(data)),
app_.config(),
mReason == Reason::SHARD ? *app_.shardFamily()
: app_.family());
mReason == Reason::SHARD ? *app_.getShardFamily()
: app_.getNodeFamily());
if (mLedger->info().hash != mHash ||
(mSeq != 0 && mSeq != mLedger->info().seq))
{
@@ -564,8 +564,8 @@ InboundLedger::trigger(std::shared_ptr<Peer> const& peer, TriggerReason reason)
if (!mHaveHeader)
{
tryDB(
mReason == Reason::SHARD ? app_.shardFamily()->db()
: app_.family().db());
mReason == Reason::SHARD ? app_.getShardFamily()->db()
: app_.getNodeFamily().db());
if (mFailed)
{
JLOG(m_journal.warn()) << " failed local for " << mHash;
@@ -866,7 +866,8 @@ InboundLedger::takeHeader(std::string const& data)
if (mComplete || mFailed || mHaveHeader)
return true;
auto* f = mReason == Reason::SHARD ? app_.shardFamily() : &app_.family();
auto* f = mReason == Reason::SHARD ? app_.getShardFamily()
: &app_.getNodeFamily();
mLedger = std::make_shared<Ledger>(
deserializeHeader(makeSlice(data)), app_.config(), *f);
if (mLedger->info().hash != mHash ||

View File

@@ -75,7 +75,7 @@ public:
, m_gotSet(std::move(gotSet))
{
m_zeroSet.mSet = std::make_shared<SHAMap>(
SHAMapType::TRANSACTION, uint256(), app_.family());
SHAMapType::TRANSACTION, uint256(), app_.getNodeFamily());
m_zeroSet.mSet->setUnbacked();
}

View File

@@ -735,18 +735,19 @@ LedgerMaster::tryFill(Job& job, std::shared_ptr<Ledger const> ledger)
void
LedgerMaster::getFetchPack(LedgerIndex missing, InboundLedger::Reason reason)
{
LedgerIndex ledgerIndex{missing + 1};
if (reason == InboundLedger::Reason::SHARD)
{
// Do not acquire a ledger sequence greater
// than the last ledger in the shard
auto const shardStore{app_.getShardStore()};
auto const shardIndex{shardStore->seqToShardIndex(missing)};
ledgerIndex =
std::min(ledgerIndex, shardStore->lastLedgerSeq(shardIndex));
}
LedgerIndex const ledgerIndex([&]() {
if (reason == InboundLedger::Reason::SHARD)
{
// Do not acquire a ledger sequence greater
// than the last ledger in the shard
auto const shardStore{app_.getShardStore()};
auto const shardIndex{shardStore->seqToShardIndex(missing)};
return std::min(missing + 1, shardStore->lastLedgerSeq(shardIndex));
}
return missing + 1;
}());
auto haveHash{getLedgerHashForHistory(ledgerIndex, reason)};
auto const haveHash{getLedgerHashForHistory(ledgerIndex, reason)};
if (!haveHash || haveHash->isZero())
{
if (reason == InboundLedger::Reason::SHARD)

View File

@@ -43,8 +43,8 @@ TransactionAcquire::TransactionAcquire(Application& app, uint256 const& hash)
: PeerSet(app, hash, TX_ACQUIRE_TIMEOUT, app.journal("TransactionAcquire"))
, mHaveRoot(false)
{
mMap =
std::make_shared<SHAMap>(SHAMapType::TRANSACTION, hash, app_.family());
mMap = std::make_shared<SHAMap>(
SHAMapType::TRANSACTION, hash, app_.getNodeFamily());
mMap->setUnbacked();
}

View File

@@ -29,10 +29,10 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/main/BasicApp.h>
#include <ripple/app/main/DBInit.h>
#include <ripple/app/main/GRPCServer.h>
#include <ripple/app/main/LoadManager.h>
#include <ripple/app/main/NodeIdentity.h>
#include <ripple/app/main/NodeStoreScheduler.h>
#include <ripple/app/main/Tuning.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/misc/LoadFeeTrack.h>
@@ -64,8 +64,9 @@
#include <ripple/resource/Fees.h>
#include <ripple/rpc/ShardArchiveHandler.h>
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/shamap/NodeFamily.h>
#include <ripple/shamap/ShardFamily.h>
#include <ripple/app/main/GRPCServer.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/system/error_code.hpp>
@@ -77,174 +78,6 @@
namespace ripple {
//------------------------------------------------------------------------------
namespace detail {
class AppFamily : public Family
{
private:
Application& app_;
TreeNodeCache treecache_;
FullBelowCache fullbelow_;
NodeStore::Database& db_;
bool const shardBacked_;
beast::Journal const j_;
// missing node handler
LedgerIndex maxSeq = 0;
std::mutex maxSeqLock;
void
acquire(uint256 const& hash, std::uint32_t seq)
{
if (hash.isNonZero())
{
auto j = app_.journal("Ledger");
JLOG(j.error()) << "Missing node in " << to_string(hash);
app_.getInboundLedgers().acquire(
hash,
seq,
shardBacked_ ? InboundLedger::Reason::SHARD
: InboundLedger::Reason::GENERIC);
}
}
public:
AppFamily(AppFamily const&) = delete;
AppFamily&
operator=(AppFamily const&) = delete;
AppFamily(
Application& app,
NodeStore::Database& db,
CollectorManager& collectorManager)
: app_(app)
, treecache_(
"TreeNodeCache",
65536,
std::chrono::minutes{1},
stopwatch(),
app.journal("TaggedCache"))
, fullbelow_(
"full_below",
stopwatch(),
collectorManager.collector(),
fullBelowTargetSize,
fullBelowExpiration)
, db_(db)
, shardBacked_(dynamic_cast<NodeStore::DatabaseShard*>(&db) != nullptr)
, j_(app.journal("SHAMap"))
{
}
beast::Journal const&
journal() override
{
return j_;
}
FullBelowCache&
fullbelow() override
{
return fullbelow_;
}
FullBelowCache const&
fullbelow() const override
{
return fullbelow_;
}
TreeNodeCache&
treecache() override
{
return treecache_;
}
TreeNodeCache const&
treecache() const override
{
return treecache_;
}
NodeStore::Database&
db() override
{
return db_;
}
NodeStore::Database const&
db() const override
{
return db_;
}
bool
isShardBacked() const override
{
return shardBacked_;
}
void
missing_node(std::uint32_t seq) override
{
auto j = app_.journal("Ledger");
JLOG(j.error()) << "Missing node in " << seq;
// prevent recursive invocation
std::unique_lock<std::mutex> lock(maxSeqLock);
if (maxSeq == 0)
{
maxSeq = seq;
do
{
// Try to acquire the most recent missing ledger
seq = maxSeq;
lock.unlock();
// This can invoke the missing node handler
acquire(app_.getLedgerMaster().getHashBySeq(seq), seq);
lock.lock();
} while (maxSeq != seq);
}
else if (maxSeq < seq)
{
// We found a more recent ledger with a
// missing node
maxSeq = seq;
}
}
void
missing_node(uint256 const& hash, std::uint32_t seq) override
{
acquire(hash, seq);
}
void
reset() override
{
{
std::lock_guard lock(maxSeqLock);
maxSeq = 0;
}
fullbelow_.reset();
treecache_.reset();
}
};
} // namespace detail
//------------------------------------------------------------------------------
// VFALCO TODO Move the function definitions into the class declaration
class ApplicationImp : public Application, public RootStoppable, public BasicApp
{
@@ -343,9 +176,9 @@ public:
// These are Stoppable-related
std::unique_ptr<JobQueue> m_jobQueue;
std::unique_ptr<NodeStore::Database> m_nodeStore;
detail::AppFamily family_;
NodeFamily nodeFamily_;
std::unique_ptr<NodeStore::DatabaseShard> shardStore_;
std::unique_ptr<detail::AppFamily> shardFamily_;
std::unique_ptr<ShardFamily> shardFamily_;
std::unique_ptr<RPC::ShardArchiveHandler> shardArchiveHandler_;
// VFALCO TODO Make OrderBookDB abstract
OrderBookDB m_orderBookDB;
@@ -476,7 +309,7 @@ public:
, m_nodeStore(m_shaMapStore->makeNodeStore("NodeStore.main", 4))
, family_(*this, *m_nodeStore, *m_collectorManager)
, nodeFamily_(*this, *m_collectorManager)
// The shard store is optional and make_ShardStore can return null.
, shardStore_(make_ShardStore(
@@ -671,13 +504,15 @@ public:
}
Family&
family() override
getNodeFamily() override
{
return family_;
return nodeFamily_;
}
// The shard store is an optional feature. If the sever is configured for
// shards, this function will return a valid pointer, otherwise a nullptr.
Family*
shardFamily() override
getShardFamily() override
{
return shardFamily_.get();
}
@@ -779,6 +614,8 @@ public:
return *m_nodeStore;
}
// The shard store is an optional feature. If the sever is configured for
// shards, this function will return a valid pointer, otherwise a nullptr.
NodeStore::DatabaseShard*
getShardStore() override
{
@@ -1127,11 +964,6 @@ public:
config_->getValueFor(SizedItem::ledgerSize),
seconds{config_->getValueFor(SizedItem::ledgerAge)});
family().treecache().setTargetSize(
config_->getValueFor(SizedItem::treeCacheSize));
family().treecache().setTargetAge(
seconds{config_->getValueFor(SizedItem::treeCacheAge)});
return true;
}
@@ -1372,9 +1204,9 @@ public:
// VFALCO TODO fix the dependency inversion using an observer,
// have listeners register for "onSweep ()" notification.
family().fullbelow().sweep();
nodeFamily_.sweep();
if (shardFamily_)
shardFamily_->fullbelow().sweep();
shardFamily_->sweep();
getMasterTransaction().sweep();
getNodeStore().sweep();
if (shardStore_)
@@ -1384,9 +1216,6 @@ public:
getValidations().expire();
getInboundLedgers().sweep();
m_acceptedLedgerCache.sweep();
family().treecache().sweep();
if (shardFamily_)
shardFamily_->treecache().sweep();
cachedSLEs_.expire();
// Set timer to do another sweep later.
@@ -1491,14 +1320,8 @@ ApplicationImp::setup()
if (shardStore_)
{
shardFamily_ = std::make_unique<detail::AppFamily>(
*this, *shardStore_, *m_collectorManager);
using namespace std::chrono;
shardFamily_->treecache().setTargetSize(
config_->getValueFor(SizedItem::treeCacheSize));
shardFamily_->treecache().setTargetAge(
seconds{config_->getValueFor(SizedItem::treeCacheAge)});
shardFamily_ =
std::make_unique<ShardFamily>(*this, *m_collectorManager);
if (!shardStore_->init())
return false;
@@ -1906,7 +1729,7 @@ ApplicationImp::startGenesisLedger()
: std::vector<uint256>{};
std::shared_ptr<Ledger> const genesis = std::make_shared<Ledger>(
create_genesis, *config_, initialAmendments, family());
create_genesis, *config_, initialAmendments, nodeFamily_);
m_ledgerMaster->storeLedger(genesis);
auto const next =
@@ -2037,7 +1860,7 @@ ApplicationImp::loadLedgerFromFile(std::string const& name)
}
auto loadLedger =
std::make_shared<Ledger>(seq, closeTime, *config_, family());
std::make_shared<Ledger>(seq, closeTime, *config_, nodeFamily_);
loadLedger->setTotalDrops(totalDrops);
for (Json::UInt index = 0; index < ledger.get().size(); ++index)

View File

@@ -147,9 +147,9 @@ public:
virtual CollectorManager&
getCollectorManager() = 0;
virtual Family&
family() = 0;
getNodeFamily() = 0;
virtual Family*
shardFamily() = 0;
getShardFamily() = 0;
virtual TimeKeeper&
timeKeeper() = 0;
virtual JobQueue&

View File

@@ -319,8 +319,8 @@ SHAMapStoreImp::run()
LedgerIndex lastRotated = state_db_.getState().lastRotated;
netOPs_ = &app_.getOPs();
ledgerMaster_ = &app_.getLedgerMaster();
fullBelowCache_ = &app_.family().fullbelow();
treeNodeCache_ = &app_.family().treecache();
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache(0));
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache(0));
transactionDb_ = &app_.getTxnDB();
ledgerDb_ = &app_.getLedgerDB();

View File

@@ -475,7 +475,7 @@ DatabaseShardImp::fetchLedger(uint256 const& hash, std::uint32_t seq)
auto ledger{std::make_shared<Ledger>(
deserializePrefixedHeader(makeSlice(nObj->getData())),
app_.config(),
*app_.shardFamily())};
*app_.getShardFamily())};
if (ledger->info().seq != seq)
{
@@ -600,7 +600,7 @@ DatabaseShardImp::validate()
shard->finalize(true, boost::none);
}
app_.shardFamily()->reset();
app_.getShardFamily()->reset();
}
void
@@ -742,7 +742,6 @@ DatabaseShardImp::import(Database& source)
}
// Create the new shard
app_.shardFamily()->reset();
auto shard{std::make_unique<Shard>(app_, *this, shardIndex, j_)};
if (!shard->open(scheduler_, *ctx_))
continue;

View File

@@ -184,7 +184,7 @@ Shard::open(Scheduler& scheduler, nudb::context& ctx)
}
if (boost::icl::length(storedSeqs) == maxLedgers_)
// All ledgers have been acquired, shard is complete
// All ledgers have been acquired, shard backend is complete
backendComplete_ = true;
}
}
@@ -238,7 +238,7 @@ Shard::prepare()
if (backendComplete_)
{
JLOG(j_.warn()) << "shard " << index_
<< " prepare called when shard is complete";
<< " prepare called when shard backend is complete";
return {};
}
@@ -417,7 +417,7 @@ Shard::finalize(
{
std::unique_lock lock(mutex_);
if (!backendComplete_)
return fail("incomplete");
return fail("backend incomplete");
/*
TODO MP
@@ -535,7 +535,7 @@ Shard::finalize(
ledger = std::make_shared<Ledger>(
deserializePrefixedHeader(makeSlice(nObj->getData())),
app_.config(),
*app_.shardFamily());
*app_.getShardFamily());
if (ledger->info().seq != seq)
return fail("invalid ledger sequence");
if (ledger->info().hash != hash)

View File

@@ -214,7 +214,7 @@ private:
std::unique_ptr<DatabaseCon> txSQLiteDB_;
// Tracking information used only when acquiring a shard from the network.
// If the shard is complete, this member will be null.
// If the shard is final, this member will be null.
std::unique_ptr<AcquireInfo> acquireInfo_;
beast::Journal const j_;

View File

@@ -32,6 +32,7 @@
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/jss.h>
#include <ripple/rpc/Context.h>
#include <ripple/shamap/ShardFamily.h>
namespace ripple {
@@ -103,9 +104,11 @@ getCountsJson(Application& app, int minObjectCount)
ret[jss::AL_hit_rate] = app.getAcceptedLedgerCache().getHitRate();
ret[jss::fullbelow_size] =
static_cast<int>(app.family().fullbelow().size());
ret[jss::treenode_cache_size] = app.family().treecache().getCacheSize();
ret[jss::treenode_track_size] = app.family().treecache().getTrackSize();
static_cast<int>(app.getNodeFamily().getFullBelowCache(0)->size());
ret[jss::treenode_cache_size] =
app.getNodeFamily().getTreeNodeCache(0)->getCacheSize();
ret[jss::treenode_track_size] =
app.getNodeFamily().getTreeNodeCache(0)->getTrackSize();
std::string uptime;
auto s = UptimeClock::now();
@@ -125,13 +128,13 @@ getCountsJson(Application& app, int minObjectCount)
if (auto shardStore = app.getShardStore())
{
auto shardFamily{dynamic_cast<ShardFamily*>(app.getShardFamily())};
auto const [cacheSz, trackSz] = shardFamily->getTreeNodeCacheSize();
Json::Value& jv = (ret[jss::shards] = Json::objectValue);
jv[jss::fullbelow_size] =
static_cast<int>(app.shardFamily()->fullbelow().size());
jv[jss::treenode_cache_size] =
app.shardFamily()->treecache().getCacheSize();
jv[jss::treenode_track_size] =
app.shardFamily()->treecache().getTrackSize();
jv[jss::fullbelow_size] = shardFamily->getFullBelowCacheSize();
jv[jss::treenode_cache_size] = cacheSz;
jv[jss::treenode_track_size] = trackSz;
ret[jss::write_load] = shardStore->getWriteLoad();
ret[jss::node_hit_rate] = shardStore->getCacheHitRate();
jv[jss::node_writes] = shardStore->getStoreCount();

View File

@@ -32,37 +32,54 @@ namespace ripple {
class Family
{
public:
Family(Family const&) = delete;
Family(Family&&) = delete;
Family&
operator=(Family const&) = delete;
Family&
operator=(Family&&) = delete;
explicit Family() = default;
virtual ~Family() = default;
virtual beast::Journal const&
journal() = 0;
virtual FullBelowCache&
fullbelow() = 0;
virtual FullBelowCache const&
fullbelow() const = 0;
virtual TreeNodeCache&
treecache() = 0;
virtual TreeNodeCache const&
treecache() const = 0;
virtual NodeStore::Database&
db() = 0;
virtual NodeStore::Database const&
db() const = 0;
virtual beast::Journal const&
journal() = 0;
/** Return a pointer to the Family Full Below Cache
@param ledgerSeq ledger sequence determines a corresponding shard cache
@note ledgerSeq is used by ShardFamily and ignored by NodeFamily
*/
virtual std::shared_ptr<FullBelowCache>
getFullBelowCache(std::uint32_t ledgerSeq) = 0;
/** Return a pointer to the Family Tree Node Cache
@param ledgerSeq ledger sequence determines a corresponding shard cache
@note ledgerSeq is used by ShardFamily and ignored by NodeFamily
*/
virtual std::shared_ptr<TreeNodeCache>
getTreeNodeCache(std::uint32_t ledgerSeq) = 0;
virtual void
sweep() = 0;
virtual bool
isShardBacked() const = 0;
virtual void
missing_node(std::uint32_t refNum) = 0;
missingNode(std::uint32_t refNum) = 0;
virtual void
missing_node(uint256 const& refHash, std::uint32_t refNum) = 0;
missingNode(uint256 const& refHash, std::uint32_t refNum) = 0;
virtual void
reset() = 0;

View File

@@ -0,0 +1,112 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_SHAMAP_NODEFAMILY_H_INCLUDED
#define RIPPLE_SHAMAP_NODEFAMILY_H_INCLUDED
#include <ripple/app/main/CollectorManager.h>
#include <ripple/shamap/Family.h>
namespace ripple {
class Application;
class NodeFamily : public Family
{
public:
NodeFamily() = delete;
NodeFamily(NodeFamily const&) = delete;
NodeFamily(NodeFamily&&) = delete;
NodeFamily&
operator=(NodeFamily const&) = delete;
NodeFamily&
operator=(NodeFamily&&) = delete;
NodeFamily(Application& app, CollectorManager& cm);
NodeStore::Database&
db() override
{
return db_;
}
NodeStore::Database const&
db() const override
{
return db_;
}
beast::Journal const&
journal() override
{
return j_;
}
bool
isShardBacked() const override
{
return false;
}
std::shared_ptr<FullBelowCache> getFullBelowCache(std::uint32_t) override
{
return fbCache_;
}
std::shared_ptr<TreeNodeCache> getTreeNodeCache(std::uint32_t) override
{
return tnCache_;
}
void
sweep() override;
void
reset() override;
void
missingNode(std::uint32_t seq) override;
void
missingNode(uint256 const& hash, std::uint32_t seq) override
{
acquire(hash, seq);
}
private:
Application& app_;
NodeStore::Database& db_;
beast::Journal const j_;
std::shared_ptr<FullBelowCache> fbCache_;
std::shared_ptr<TreeNodeCache> tnCache_;
// Missing node handler
LedgerIndex maxSeq_{0};
std::mutex maxSeqMutex_;
void
acquire(uint256 const& hash, std::uint32_t seq);
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,124 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_SHAMAP_SHARDFAMILY_H_INCLUDED
#define RIPPLE_SHAMAP_SHARDFAMILY_H_INCLUDED
#include <ripple/app/main/CollectorManager.h>
#include <ripple/shamap/Family.h>
namespace ripple {
class Application;
class ShardFamily : public Family
{
public:
ShardFamily() = delete;
ShardFamily(ShardFamily const&) = delete;
ShardFamily(ShardFamily&&) = delete;
ShardFamily&
operator=(ShardFamily const&) = delete;
ShardFamily&
operator=(ShardFamily&&) = delete;
ShardFamily(Application& app, CollectorManager& cm);
NodeStore::Database&
db() override
{
return db_;
}
NodeStore::Database const&
db() const override
{
return db_;
}
beast::Journal const&
journal() override
{
return j_;
}
bool
isShardBacked() const override
{
return true;
}
std::shared_ptr<FullBelowCache>
getFullBelowCache(std::uint32_t ledgerSeq) override;
/** Return the number of entries in the cache */
int
getFullBelowCacheSize();
std::shared_ptr<TreeNodeCache>
getTreeNodeCache(std::uint32_t ledgerSeq) override;
/** Return a pair where the first item is the number of items cached
and the second item is the number of entries in the cached
*/
std::pair<int, int>
getTreeNodeCacheSize();
void
sweep() override;
void
reset() override;
void
missingNode(std::uint32_t seq) override;
void
missingNode(uint256 const& hash, std::uint32_t seq) override
{
acquire(hash, seq);
}
private:
Application& app_;
NodeStore::Database& db_;
CollectorManager& cm_;
beast::Journal const j_;
std::unordered_map<std::uint32_t, std::shared_ptr<FullBelowCache>> fbCache_;
std::mutex fbCacheMutex_;
std::unordered_map<std::uint32_t, std::shared_ptr<TreeNodeCache>> tnCache_;
std::mutex tnCacheMutex_;
int const tnTargetSize_;
std::chrono::seconds const tnTargetAge_;
// Missing node handler
LedgerIndex maxSeq_{0};
std::mutex maxSeqMutex_;
void
acquire(uint256 const& hash, std::uint32_t seq);
};
} // namespace ripple
#endif

View File

@@ -21,12 +21,9 @@
#define RIPPLE_SHAMAP_TREENODECACHE_H_INCLUDED
#include <ripple/shamap/SHAMapTreeNode.h>
#include <ripple/shamap/TreeNodeCache.h>
namespace ripple {
class SHAMapAbstractNode;
using TreeNodeCache = TaggedCache<uint256, SHAMapAbstractNode>;
} // namespace ripple

View File

@@ -0,0 +1,108 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/main/Tuning.h>
#include <ripple/shamap/NodeFamily.h>
namespace ripple {
NodeFamily::NodeFamily(Application& app, CollectorManager& cm)
: app_(app)
, db_(app.getNodeStore())
, j_(app.journal("NodeFamily"))
, fbCache_(std::make_shared<FullBelowCache>(
"Node family full below cache",
stopwatch(),
cm.collector(),
fullBelowTargetSize,
fullBelowExpiration))
, tnCache_(std::make_shared<TreeNodeCache>(
"Node family tree node cache",
app.config().getValueFor(SizedItem::treeCacheSize),
std::chrono::seconds(
app.config().getValueFor(SizedItem::treeCacheAge)),
stopwatch(),
j_))
{
}
void
NodeFamily::sweep()
{
fbCache_->sweep();
tnCache_->sweep();
}
void
NodeFamily::reset()
{
{
std::lock_guard lock(maxSeqMutex_);
maxSeq_ = 0;
}
fbCache_->reset();
tnCache_->reset();
}
void
NodeFamily::missingNode(std::uint32_t seq)
{
JLOG(j_.error()) << "Missing node in " << seq;
std::unique_lock<std::mutex> lock(maxSeqMutex_);
if (maxSeq_ == 0)
{
maxSeq_ = seq;
do
{
// Try to acquire the most recent missing ledger
seq = maxSeq_;
lock.unlock();
// This can invoke the missing node handler
acquire(app_.getLedgerMaster().getHashBySeq(seq), seq);
lock.lock();
} while (maxSeq_ != seq);
}
else if (maxSeq_ < seq)
{
// We found a more recent ledger with a missing node
maxSeq_ = seq;
}
}
void
NodeFamily::acquire(uint256 const& hash, std::uint32_t seq)
{
if (hash.isNonZero())
{
JLOG(j_.error()) << "Missing node in " << to_string(hash);
app_.getInboundLedgers().acquire(
hash, seq, InboundLedger::Reason::GENERIC);
}
}
} // namespace ripple

View File

@@ -164,7 +164,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
}
else if (full_)
{
f_.missing_node(ledgerSeq_);
f_.missingNode(ledgerSeq_);
const_cast<bool&>(full_) = false;
}
}
@@ -332,10 +332,10 @@ SHAMap::descend(
assert(!parent->isEmptyBranch(branch));
SHAMapAbstractNode* child = parent->getChildPointer(branch);
auto const& childHash = parent->getChildHash(branch);
if (!child)
{
auto const& childHash = parent->getChildHash(branch);
std::shared_ptr<SHAMapAbstractNode> childNode =
fetchNodeNT(childHash, filter);
@@ -1115,7 +1115,7 @@ SHAMap::dump(bool hash) const
std::shared_ptr<SHAMapAbstractNode>
SHAMap::getCache(SHAMapHash const& hash) const
{
auto ret = f_.treecache().fetch(hash.as_uint256());
auto ret = f_.getTreeNodeCache(ledgerSeq_)->fetch(hash.as_uint256());
assert(!ret || !ret->getSeq());
return ret;
}
@@ -1129,7 +1129,8 @@ SHAMap::canonicalize(
assert(node->getSeq() == 0);
assert(node->getNodeHash() == hash);
f_.treecache().canonicalize_replace_client(hash.as_uint256(), node);
f_.getTreeNodeCache(ledgerSeq_)
->canonicalize_replace_client(hash.as_uint256(), node);
}
void

View File

@@ -198,7 +198,9 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
fullBelow = false;
}
else if (
!backed_ || !f_.fullbelow().touch_if_exists(childHash.as_uint256()))
!backed_ ||
!f_.getFullBelowCache(ledgerSeq_)
->touch_if_exists(childHash.as_uint256()))
{
SHAMapNodeID childID = nodeID.getChildNodeID(branch);
bool pending = false;
@@ -243,7 +245,10 @@ SHAMap::gmn_ProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
{ // No partial node encountered below this node
node->setFullBelowGen(mn.generation_);
if (backed_)
f_.fullbelow().insert(node->getNodeHash().as_uint256());
{
f_.getFullBelowCache(ledgerSeq_)
->insert(node->getNodeHash().as_uint256());
}
}
node = nullptr;
@@ -323,7 +328,7 @@ SHAMap::getMissingNodes(int max, SHAMapSyncFilter* filter)
max,
filter,
f_.db().getDesiredAsyncReadCount(ledgerSeq_),
f_.fullbelow().getGeneration());
f_.getFullBelowCache(ledgerSeq_)->getGeneration());
if (!root_->isInner() ||
std::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow(
@@ -599,7 +604,7 @@ SHAMap::addKnownNode(
return SHAMapAddNode::duplicate();
}
std::uint32_t generation = f_.fullbelow().getGeneration();
auto const generation = f_.getFullBelowCache(ledgerSeq_)->getGeneration();
auto newNode = SHAMapAbstractNode::makeFromWire(rawNode);
SHAMapNodeID iNodeID;
auto iNode = root_.get();
@@ -618,8 +623,11 @@ SHAMap::addKnownNode(
}
auto childHash = inner->getChildHash(branch);
if (f_.fullbelow().touch_if_exists(childHash.as_uint256()))
if (f_.getFullBelowCache(ledgerSeq_)
->touch_if_exists(childHash.as_uint256()))
{
return SHAMapAddNode::duplicate();
}
auto prevNode = inner;
std::tie(iNode, iNodeID) = descend(inner, iNodeID, branch, filter);

View File

@@ -0,0 +1,195 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/main/Tuning.h>
#include <ripple/nodestore/DatabaseShard.h>
#include <ripple/shamap/ShardFamily.h>
namespace ripple {
static NodeStore::Database&
getShardStore(Application& app)
{
auto const dbPtr = app.getShardStore();
assert(dbPtr);
return *dbPtr;
}
ShardFamily::ShardFamily(Application& app, CollectorManager& cm)
: app_(app)
, db_(getShardStore(app))
, cm_(cm)
, j_(app.journal("ShardFamily"))
, tnTargetSize_(app.config().getValueFor(SizedItem::treeCacheSize))
, tnTargetAge_(app.config().getValueFor(SizedItem::treeCacheAge))
{
}
std::shared_ptr<FullBelowCache>
ShardFamily::getFullBelowCache(std::uint32_t ledgerSeq)
{
auto const shardIndex{app_.getShardStore()->seqToShardIndex(ledgerSeq)};
std::lock_guard lock(fbCacheMutex_);
if (auto const it{fbCache_.find(shardIndex)}; it != fbCache_.end())
return it->second;
// Create a cache for the corresponding shard
auto fbCache{std::make_shared<FullBelowCache>(
"Shard family full below cache shard " + std::to_string(shardIndex),
stopwatch(),
cm_.collector(),
fullBelowTargetSize,
fullBelowExpiration)};
return fbCache_.emplace(shardIndex, std::move(fbCache)).first->second;
}
int
ShardFamily::getFullBelowCacheSize()
{
size_t sz{0};
std::lock_guard lock(fbCacheMutex_);
for (auto const& e : fbCache_)
sz += e.second->size();
return sz;
}
std::shared_ptr<TreeNodeCache>
ShardFamily::getTreeNodeCache(std::uint32_t ledgerSeq)
{
auto const shardIndex{app_.getShardStore()->seqToShardIndex(ledgerSeq)};
std::lock_guard lock(tnCacheMutex_);
if (auto const it{tnCache_.find(shardIndex)}; it != tnCache_.end())
return it->second;
// Create a cache for the corresponding shard
auto tnCache{std::make_shared<TreeNodeCache>(
"Shard family tree node cache shard " + std::to_string(shardIndex),
tnTargetSize_,
tnTargetAge_,
stopwatch(),
j_)};
return tnCache_.emplace(shardIndex, std::move(tnCache)).first->second;
}
std::pair<int, int>
ShardFamily::getTreeNodeCacheSize()
{
int cacheSz{0};
int trackSz{0};
std::lock_guard lock(tnCacheMutex_);
for (auto const& e : tnCache_)
{
cacheSz += e.second->getCacheSize();
trackSz += e.second->getTrackSize();
}
return {cacheSz, trackSz};
}
void
ShardFamily::sweep()
{
{
std::lock_guard lock(fbCacheMutex_);
for (auto it = fbCache_.cbegin(); it != fbCache_.cend();)
{
it->second->sweep();
// Remove cache if empty
if (it->second->size() == 0)
it = fbCache_.erase(it);
else
++it;
}
}
std::lock_guard lock(tnCacheMutex_);
for (auto it = tnCache_.cbegin(); it != tnCache_.cend();)
{
it->second->sweep();
// Remove cache if empty
if (it->second->getTrackSize() == 0)
it = tnCache_.erase(it);
else
++it;
}
}
void
ShardFamily::reset()
{
{
std::lock_guard lock(maxSeqMutex_);
maxSeq_ = 0;
}
{
std::lock_guard lock(fbCacheMutex_);
fbCache_.clear();
}
std::lock_guard lock(tnCacheMutex_);
tnCache_.clear();
}
void
ShardFamily::missingNode(std::uint32_t seq)
{
JLOG(j_.error()) << "Missing node in ledger sequence " << seq;
std::unique_lock<std::mutex> lock(maxSeqMutex_);
if (maxSeq_ == 0)
{
maxSeq_ = seq;
do
{
// Try to acquire the most recent missing ledger
seq = maxSeq_;
lock.unlock();
// This can invoke the missing node handler
acquire(app_.getLedgerMaster().getHashBySeq(seq), seq);
lock.lock();
} while (maxSeq_ != seq);
}
else if (maxSeq_ < seq)
{
// We found a more recent ledger with a missing node
maxSeq_ = seq;
}
}
void
ShardFamily::acquire(uint256 const& hash, std::uint32_t seq)
{
if (hash.isNonZero())
{
JLOG(j_.error()) << "Missing node in " << to_string(hash);
app_.getInboundLedgers().acquire(
hash, seq, InboundLedger::Reason::SHARD);
}
}
} // namespace ripple

View File

@@ -57,7 +57,7 @@ public:
create_genesis,
env.app().config(),
std::vector<uint256>{},
env.app().family());
env.app().getNodeFamily());
}
auto res = std::make_shared<Ledger>(
*prev, prev->info().closeTime + closeOffset);

View File

@@ -77,7 +77,10 @@ class RCLValidations_test : public beast::unit_test::suite
jtx::Env env(*this);
Config config;
auto prev = std::make_shared<Ledger const>(
create_genesis, config, std::vector<uint256>{}, env.app().family());
create_genesis,
config,
std::vector<uint256>{},
env.app().getNodeFamily());
history.push_back(prev);
for (auto i = 0; i < (2 * maxAncestors + 1); ++i)
{
@@ -237,7 +240,10 @@ class RCLValidations_test : public beast::unit_test::suite
auto& j = env.journal;
Config config;
auto prev = std::make_shared<Ledger const>(
create_genesis, config, std::vector<uint256>{}, env.app().family());
create_genesis,
config,
std::vector<uint256>{},
env.app().getNodeFamily());
history.push_back(prev);
for (auto i = 0; i < (maxAncestors + 10); ++i)
{

View File

@@ -60,7 +60,7 @@ struct Regression_test : public beast::unit_test::suite
create_genesis,
env.app().config(),
std::vector<uint256>{},
env.app().family());
env.app().getNodeFamily());
auto expectedDrops = INITIAL_XRP;
BEAST_EXPECT(closed->info().drops == expectedDrops);

View File

@@ -39,7 +39,7 @@ class SkipList_test : public beast::unit_test::suite
create_genesis,
config,
std::vector<uint256>{},
env.app().family());
env.app().getNodeFamily());
history.push_back(prev);
for (auto i = 0; i < 1023; ++i)
{

View File

@@ -134,7 +134,10 @@ class View_test : public beast::unit_test::suite
Env env(*this);
Config config;
std::shared_ptr<Ledger const> const genesis = std::make_shared<Ledger>(
create_genesis, config, std::vector<uint256>{}, env.app().family());
create_genesis,
config,
std::vector<uint256>{},
env.app().getNodeFamily());
auto const ledger = std::make_shared<Ledger>(
*genesis, env.app().timeKeeper().closeTime());
wipe(*ledger);
@@ -388,7 +391,10 @@ class View_test : public beast::unit_test::suite
Env env(*this);
Config config;
std::shared_ptr<Ledger const> const genesis = std::make_shared<Ledger>(
create_genesis, config, std::vector<uint256>{}, env.app().family());
create_genesis,
config,
std::vector<uint256>{},
env.app().getNodeFamily());
auto const ledger = std::make_shared<Ledger>(
*genesis, env.app().timeKeeper().closeTime());
auto setup123 = [&ledger, this]() {
@@ -769,7 +775,7 @@ class View_test : public beast::unit_test::suite
create_genesis,
config,
std::vector<uint256>{},
env.app().family());
env.app().getNodeFamily());
auto const ledger = std::make_shared<Ledger>(
*genesis, env.app().timeKeeper().closeTime());
wipe(*ledger);

View File

@@ -118,7 +118,7 @@ public:
using namespace beast::severities;
test::SuiteJournal journal("FetchPack_test", *this);
TestFamily f(journal);
TestNodeFamily f(journal);
std::shared_ptr<Table> t1(std::make_shared<Table>(SHAMapType::FREE, f));
pass();

View File

@@ -91,7 +91,7 @@ public:
using namespace beast::severities;
test::SuiteJournal journal("SHAMapSync_test", *this);
TestFamily f(journal), f2(journal);
TestNodeFamily f(journal), f2(journal);
SHAMap source(SHAMapType::FREE, f);
SHAMap destination(SHAMapType::FREE, f2);

View File

@@ -139,7 +139,7 @@ public:
else
testcase("add/traverse unbacked");
tests::TestFamily f(journal);
tests::TestNodeFamily f(journal);
// h3 and h4 differ only in the leaf, same terminal node (level 19)
uint256 h1, h2, h3, h4, h5;
@@ -327,7 +327,7 @@ public:
"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
"a8");
tests::TestFamily tf{journal};
tests::TestNodeFamily tf{journal};
SHAMap map{SHAMapType::FREE, tf};
if (!backed)
map.setUnbacked();

View File

@@ -29,22 +29,31 @@
namespace ripple {
namespace tests {
class TestFamily : public Family
class TestNodeFamily : public Family
{
private:
std::unique_ptr<NodeStore::Database> db_;
std::shared_ptr<FullBelowCache> fbCache_;
std::shared_ptr<TreeNodeCache> tnCache_;
TestStopwatch clock_;
NodeStore::DummyScheduler scheduler_;
TreeNodeCache treecache_;
FullBelowCache fullbelow_;
RootStoppable parent_;
std::unique_ptr<NodeStore::Database> db_;
bool shardBacked_;
beast::Journal j_;
beast::Journal const j_;
public:
TestFamily(beast::Journal j)
: treecache_("TreeNodeCache", 65536, std::chrono::minutes{1}, clock_, j)
, fullbelow_("full_below", clock_)
TestNodeFamily(beast::Journal j)
: fbCache_(std::make_shared<FullBelowCache>(
"App family full below cache",
clock_))
, tnCache_(std::make_shared<TreeNodeCache>(
"App family tree node cache",
65536,
std::chrono::minutes{1},
clock_,
j))
, parent_("TestRootStoppable")
, j_(j)
{
@@ -53,44 +62,6 @@ public:
testSection.set("Path", "SHAMap_test");
db_ = NodeStore::Manager::instance().make_Database(
"test", scheduler_, 1, parent_, testSection, j);
shardBacked_ =
dynamic_cast<NodeStore::DatabaseShard*>(db_.get()) != nullptr;
}
beast::manual_clock<std::chrono::steady_clock>
clock()
{
return clock_;
}
beast::Journal const&
journal() override
{
return j_;
}
FullBelowCache&
fullbelow() override
{
return fullbelow_;
}
FullBelowCache const&
fullbelow() const override
{
return fullbelow_;
}
TreeNodeCache&
treecache() override
{
return treecache_;
}
TreeNodeCache const&
treecache() const override
{
return treecache_;
}
NodeStore::Database&
@@ -105,20 +76,43 @@ public:
return *db_;
}
bool
isShardBacked() const override
beast::Journal const&
journal() override
{
return shardBacked_;
return j_;
}
std::shared_ptr<FullBelowCache> getFullBelowCache(std::uint32_t) override
{
return fbCache_;
}
std::shared_ptr<TreeNodeCache> getTreeNodeCache(std::uint32_t) override
{
return tnCache_;
}
void
missing_node(std::uint32_t refNum) override
sweep() override
{
fbCache_->sweep();
tnCache_->sweep();
}
bool
isShardBacked() const override
{
return true;
}
void
missingNode(std::uint32_t refNum) override
{
Throw<std::runtime_error>("missing node");
}
void
missing_node(uint256 const& refHash, std::uint32_t refNum) override
missingNode(uint256 const& refHash, std::uint32_t refNum) override
{
Throw<std::runtime_error>("missing node");
}
@@ -126,8 +120,14 @@ public:
void
reset() override
{
fullbelow_.reset();
treecache_.reset();
fbCache_->reset();
tnCache_->reset();
}
beast::manual_clock<std::chrono::steady_clock>
clock()
{
return clock_;
}
};