mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 23:00:55 +00:00
290 lines
8.6 KiB
C++
290 lines
8.6 KiB
C++
#include <xrpld/app/ledger/detail/LedgerDeltaAcquire.h>
|
|
|
|
#include <xrpld/app/ledger/BuildLedger.h>
|
|
#include <xrpld/app/ledger/InboundLedger.h>
|
|
#include <xrpld/app/ledger/InboundLedgers.h>
|
|
#include <xrpld/app/ledger/LedgerMaster.h>
|
|
#include <xrpld/app/ledger/LedgerReplay.h>
|
|
#include <xrpld/app/ledger/LedgerReplayer.h>
|
|
#include <xrpld/app/ledger/detail/TimeoutCounter.h>
|
|
#include <xrpld/app/main/Application.h>
|
|
#include <xrpld/overlay/Peer.h>
|
|
#include <xrpld/overlay/PeerSet.h>
|
|
#include <xrpld/telemetry/MetricMacros.h>
|
|
#include <xrpld/telemetry/MetricNames.h>
|
|
|
|
#include <xrpl/basics/Log.h>
|
|
#include <xrpl/basics/base_uint.h>
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/core/Job.h>
|
|
#include <xrpl/core/JobQueue.h>
|
|
#include <xrpl/ledger/ApplyView.h>
|
|
#include <xrpl/protocol/LedgerHeader.h>
|
|
#include <xrpl/protocol/Rules.h>
|
|
|
|
#include <xrpl.pb.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace xrpl {
|
|
|
|
LedgerDeltaAcquire::LedgerDeltaAcquire(
|
|
Application& app,
|
|
InboundLedgers& inboundLedgers,
|
|
uint256 const& ledgerHash,
|
|
std::uint32_t ledgerSeq,
|
|
std::unique_ptr<PeerSet> peerSet)
|
|
: TimeoutCounter(
|
|
app,
|
|
ledgerHash,
|
|
ledger_replay_parameters::kSubTaskTimeout,
|
|
{.jobType = JtReplayTask,
|
|
.jobName = "LedReplDelta",
|
|
.jobLimit = ledger_replay_parameters::kMaxQueuedTasks},
|
|
app.getJournal("LedgerReplayDelta"))
|
|
, inboundLedgers_(inboundLedgers)
|
|
, ledgerSeq_(ledgerSeq)
|
|
, peerSet_(std::move(peerSet))
|
|
{
|
|
JLOG(journal_.trace()) << "Create " << hash_ << " Seq " << ledgerSeq;
|
|
}
|
|
|
|
LedgerDeltaAcquire::~LedgerDeltaAcquire()
|
|
{
|
|
JLOG(journal_.trace()) << "Destroy " << hash_;
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::init(int numPeers)
|
|
{
|
|
ScopedLockType sl(mtx_);
|
|
if (!isDone())
|
|
{
|
|
trigger(numPeers, sl);
|
|
setTimer(sl);
|
|
}
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::trigger(std::size_t limit, ScopedLockType& sl)
|
|
{
|
|
fullLedger_ = app_.getLedgerMaster().getLedgerByHash(hash_);
|
|
if (fullLedger_)
|
|
{
|
|
complete_ = true;
|
|
JLOG(journal_.trace()) << "existing ledger " << hash_;
|
|
notify(sl);
|
|
return;
|
|
}
|
|
|
|
if (!fallBack_)
|
|
{
|
|
peerSet_->addPeers(
|
|
limit,
|
|
[this](auto peer) {
|
|
return peer->supportsFeature(ProtocolFeature::LedgerReplay) &&
|
|
peer->hasLedger(hash_, ledgerSeq_);
|
|
},
|
|
[this](auto peer) {
|
|
if (peer->supportsFeature(ProtocolFeature::LedgerReplay))
|
|
{
|
|
JLOG(journal_.trace()) << "Add a peer " << peer->id() << " for " << hash_;
|
|
protocol::TMReplayDeltaRequest request;
|
|
request.set_ledgerhash(hash_.data(), hash_.size());
|
|
peerSet_->sendRequest(request, peer);
|
|
}
|
|
else
|
|
{
|
|
if (++noFeaturePeerCount_ >= ledger_replay_parameters::kMaxNoFeaturePeerCount)
|
|
{
|
|
JLOG(journal_.debug()) << "Fall back for " << hash_;
|
|
timerInterval_ = ledger_replay_parameters::kSubTaskFallbackTimeout;
|
|
|
|
// Same fallback as the skip-list stage, for the delta
|
|
// stage: too few replay-capable peers, so the whole
|
|
// ledger is acquired instead of just its delta. The
|
|
// `stage` label separates the two, because they fail
|
|
// for different reasons and are fixed differently.
|
|
// Emitted once, on the transition into fallback.
|
|
XRPL_METRIC_COUNTER_INC_LABELED(
|
|
app_,
|
|
telemetry::metric::ledgerReplayFallbackTotal,
|
|
"Replay sub-acquires that fell back to a full ledger acquire",
|
|
{{telemetry::label::stage,
|
|
std::string(telemetry::lval::replay_fallback::delta)}});
|
|
|
|
fallBack_ = true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (fallBack_)
|
|
inboundLedgers_.acquire(hash_, ledgerSeq_, InboundLedger::Reason::GENERIC);
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::onTimer(bool progress, ScopedLockType& sl)
|
|
{
|
|
JLOG(journal_.trace()) << "timeouts_=" << timeouts_ << " for " << hash_;
|
|
if (timeouts_ > ledger_replay_parameters::kSubTaskMaxTimeouts)
|
|
{
|
|
failed_ = true;
|
|
JLOG(journal_.debug()) << "too many timeouts " << hash_;
|
|
notify(sl);
|
|
}
|
|
else
|
|
{
|
|
trigger(1, sl);
|
|
}
|
|
}
|
|
|
|
std::weak_ptr<TimeoutCounter>
|
|
LedgerDeltaAcquire::pmDowncast()
|
|
{
|
|
return shared_from_this();
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::processData(
|
|
LedgerHeader const& info,
|
|
std::map<std::uint32_t, std::shared_ptr<STTx const>>&& orderedTxns)
|
|
{
|
|
ScopedLockType sl(mtx_);
|
|
JLOG(journal_.trace()) << "got data for " << hash_;
|
|
if (isDone())
|
|
return;
|
|
|
|
if (info.seq == ledgerSeq_)
|
|
{
|
|
// create a temporary ledger for building a LedgerReplay object later
|
|
Rules const rules{app_.config().features};
|
|
replayTemp_ = std::make_shared<Ledger>(info, rules, app_.getNodeFamily());
|
|
if (replayTemp_)
|
|
{
|
|
complete_ = true;
|
|
orderedTxns_ = std::move(orderedTxns);
|
|
JLOG(journal_.debug()) << "ready to replay " << hash_;
|
|
notify(sl);
|
|
return;
|
|
}
|
|
}
|
|
|
|
failed_ = true;
|
|
JLOG(journal_.error()) << "failed to create a (info only) ledger from verified data " << hash_;
|
|
notify(sl);
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::addDataCallback(InboundLedger::Reason reason, OnDeltaDataCB&& cb)
|
|
{
|
|
ScopedLockType sl(mtx_);
|
|
dataReadyCallbacks_.emplace_back(std::move(cb));
|
|
if (!reasons_.contains(reason))
|
|
{
|
|
reasons_.emplace(reason);
|
|
if (fullLedger_)
|
|
onLedgerBuilt(sl, reason);
|
|
}
|
|
|
|
if (isDone())
|
|
{
|
|
JLOG(journal_.debug()) << "task added to a finished LedgerDeltaAcquire " << hash_;
|
|
notify(sl);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Ledger const>
|
|
LedgerDeltaAcquire::tryBuild(std::shared_ptr<Ledger const> const& parent)
|
|
{
|
|
ScopedLockType sl(mtx_);
|
|
|
|
if (fullLedger_)
|
|
return fullLedger_;
|
|
|
|
if (failed_ || !complete_ || !replayTemp_)
|
|
return {};
|
|
|
|
XRPL_ASSERT(
|
|
parent->seq() + 1 == replayTemp_->seq(),
|
|
"xrpl::LedgerDeltaAcquire::tryBuild : parent sequence match");
|
|
XRPL_ASSERT(
|
|
parent->header().hash == replayTemp_->header().parentHash,
|
|
"xrpl::LedgerDeltaAcquire::tryBuild : parent hash match");
|
|
// build ledger
|
|
LedgerReplay const replayData(parent, replayTemp_, std::move(orderedTxns_));
|
|
fullLedger_ = buildLedger(replayData, TapNone, app_, journal_);
|
|
if (fullLedger_ && fullLedger_->header().hash == hash_)
|
|
{
|
|
JLOG(journal_.info()) << "Built " << hash_;
|
|
onLedgerBuilt(sl);
|
|
return fullLedger_;
|
|
}
|
|
|
|
failed_ = true;
|
|
complete_ = false;
|
|
JLOG(journal_.error()) << "tryBuild failed " << hash_ << " with parent "
|
|
<< parent->header().hash;
|
|
Throw<std::runtime_error>("Cannot replay ledger");
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::onLedgerBuilt(ScopedLockType& sl, std::optional<InboundLedger::Reason> reason)
|
|
{
|
|
JLOG(journal_.debug()) << "onLedgerBuilt " << hash_ << (reason ? " for a new reason" : "");
|
|
|
|
std::vector<InboundLedger::Reason> reasons(reasons_.begin(), reasons_.end());
|
|
bool firstTime = true;
|
|
if (reason) // small chance
|
|
{
|
|
reasons.clear();
|
|
reasons.push_back(*reason);
|
|
firstTime = false;
|
|
}
|
|
app_.getJobQueue().addJob(
|
|
JtReplayTask, "OnLedBuilt", [=, ledger = this->fullLedger_, &app = this->app_]() {
|
|
for (auto reason : reasons)
|
|
{
|
|
switch (reason)
|
|
{
|
|
case InboundLedger::Reason::GENERIC:
|
|
app.getLedgerMaster().storeLedger(ledger);
|
|
break;
|
|
default:
|
|
// TODO for other use cases
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (firstTime)
|
|
app.getLedgerMaster().tryAdvance();
|
|
});
|
|
}
|
|
|
|
void
|
|
LedgerDeltaAcquire::notify(ScopedLockType& sl)
|
|
{
|
|
XRPL_ASSERT(isDone(), "xrpl::LedgerDeltaAcquire::notify : is done");
|
|
std::vector<OnDeltaDataCB> toCall;
|
|
std::swap(toCall, dataReadyCallbacks_);
|
|
auto const good = !failed_;
|
|
sl.unlock();
|
|
|
|
for (auto& cb : toCall)
|
|
{
|
|
cb(good, hash_);
|
|
}
|
|
|
|
sl.lock();
|
|
}
|
|
|
|
} // namespace xrpl
|