Compare commits

...

1 Commits

Author SHA1 Message Date
Pratik Mankawde
3367f95b1e docs: Document the LedgerMaster class
LedgerMaster sits at the centre of the ledger stream but carried only a
handful of one-line comments, so the rules a caller has to obey were
only discoverable by reading the implementation.

Add Doxygen comments to every declaration in the class: what each
method does, what each parameter means, what a return value contains
on both the normal and the empty or failing path, and what each member
holds along with its unit and the lock that guards it. The class
comment adds two diagrams - one naming the collaborators, one tracing
the tryAdvance/doAdvance state machine - plus notes on thread safety,
the two-lock ordering, and which accessors can block on the network.

Also document the three things that exist only in the translation
unit: the file-scope back-fill limits, the shouldAcquire() helper, and
the ValSeq tally used while counting trusted validations.

Existing comments are reused as the wording wherever they existed, and
are only reformatted into Doxygen blocks. No code changes: both files
are byte-identical after stripping comments.
2026-09-02 14:20:52 +01:00
2 changed files with 670 additions and 21 deletions

View File

@@ -43,68 +43,226 @@ namespace xrpl {
class Peer;
class Transaction;
// Tracks the current ledger and any ledgers in the process of closing
// Tracks ledger history
// Tracks held transactions
/**
* Tracks the current ledger and any ledgers in the process of closing.
* Tracks ledger history. Tracks held transactions.
*
* Four ledgers are tracked, and they can all differ:
*
* current open ledger new transactions go into (owned by OpenLedger)
* closed most recently closed ledger, not yet fully validated
* validated highest ledger with a quorum of trusted validations
* published highest ledger handed to subscribed clients; lags validated
*
* RCLConsensus ──switchLCL/consensusBuilt──> LedgerMaster
* PeerImp ──────gotFetchPack/makeFetchPack─> │
* ├─> LedgerHistory (cache)
* ├─> InboundLedgers (acquire)
* ├─> NetworkOPs (publish)
* ├─> PathRequestManager
* ├─> LedgerReplayer
* └─> Application (everything else)
*
* Advancing the stream is a state machine run on a JobQueue thread. Callers
* only ever poke it with tryAdvance(); the loop decides what to do next:
*
* tryAdvance()
* │ (one AdvanceLedger job at a time)
* v
* doAdvance() ──> findNewLedgersToPublish()
* ^ │
* │ has ledgers? ──yes──> setFullLedger, setPubLedger,
* │ │ NetworkOPs::pubLedger, newPFWork
* │ no
* │ v
* │ caught up and idle? ──no──> stop
* │ │ yes
* │ v
* │ fetchForHistory(newest gap below pubLedger) ──> tryFill()
* └──────── repeat while progress was made
*
* @code
* // Read the validated ledger, if the node has one yet.
* if (ledgerMaster.haveValidated())
* auto const seq = ledgerMaster.getValidatedLedger()->header().seq;
*
* // After consensus closes a ledger: adopt it, then let the stream catch up.
* ledgerMaster.switchLCL(closed);
* ledgerMaster.tryAdvance();
*
* // Edge case: on a node that has never validated, the age accessors report
* // two weeks rather than zero, so "stale" checks fail closed.
* ledgerMaster.getValidatedLedgerAge(); // 2 weeks
* @endcode
*
* @note Thread-safe. Two recursive mutexes guard the state: mutex_ for the
* tracked ledgers and the job bookkeeping, completeLock_ for completeLedgers_
* only. When both are needed, mutex_ is taken first. The sequence numbers are
* atomics and are read without either lock.
* @note Several accessors can block for a long time: anything that reaches
* InboundLedgers::acquire() may wait on the network, and anything walking a
* skip list may throw SHAMapMissingNode. Do not call them while holding a lock
* a network thread needs.
* @note The published stream is best-effort. If it falls more than 100 ledgers
* behind, the gap is abandoned and publication jumps to the validated ledger.
*/
class LedgerMaster : public AbstractFetchPackContainer
{
public:
/**
* Constructs the tracker; reads history and fetch limits from the config.
*
* @param app Owning application, used to reach every other subsystem.
* @param stopwatch Clock driving expiry of the fetch-pack cache.
* @param collector Sink the ledger-age gauges are registered with.
* @param journal Log sink.
*/
explicit LedgerMaster(
Application& app,
Stopwatch& stopwatch,
beast::insight::Collector::ptr const& collector,
beast::Journal journal);
/**
* Destroys the tracker. Outstanding JobQueue work must already have been
* drained, since those jobs capture this.
*/
~LedgerMaster() override = default;
/**
* @return Sequence of the open ledger transactions are being applied to.
*/
LedgerIndex
getCurrentLedgerIndex();
/**
* @return Sequence of the last fully validated ledger, or 0 if there is none.
*/
LedgerIndex
getValidLedgerIndex();
/**
* Tests whether a view can belong to the same chain as what we validated.
*
* @param view Ledger view to test.
* @param s Stream the reason for a mismatch is logged to.
* @param reason Caller-supplied label for that log line.
* @return false when the view conflicts with the validated ledger or with
* the highest ledger known to have a validation quorum.
*/
bool
isCompatible(ReadView const&, beast::Journal::Stream, char const* reason);
/**
* Exposes the lock guarding the tracked ledgers, so a caller can hold it
* across several calls and see one consistent snapshot.
*
* @return The recursive mutex. Callers must not keep it past a blocking call.
*/
std::recursive_mutex&
peekMutex();
// The current ledger is the ledger we believe new transactions should go in
/**
* The current ledger is the ledger we believe new transactions should go in.
*
* @return The open ledger.
*/
std::shared_ptr<ReadView const>
getCurrentLedger();
// The finalized ledger is the last closed/accepted ledger
/**
* The finalized ledger is the last closed/accepted ledger.
*
* @return That ledger, or null before the first close.
*/
std::shared_ptr<Ledger const>
getClosedLedger()
{
return closedLedger_.get();
}
// The validated ledger is the last fully validated ledger.
/**
* The validated ledger is the last fully validated ledger.
*
* @return That ledger, or null if none is resident.
*/
std::shared_ptr<Ledger const>
getValidatedLedger();
// The Rules are in the last fully validated ledger if there is one.
/**
* The Rules are in the last fully validated ledger if there is one.
*
* @return Those Rules, or ones built from the configured amendments when
* nothing is validated yet.
*/
Rules
getValidatedRules();
// This is the last ledger we published to clients and can lag the validated
// ledger
/**
* This is the last ledger we published to clients and can lag the validated
* ledger.
*
* @return That ledger, or null until the first publication.
*/
std::shared_ptr<ReadView const>
getPublishedLedger();
/**
* @return Seconds between network time and the close time of the last
* published ledger; two weeks when nothing has been published, and never
* negative.
*/
std::chrono::seconds
getPublishedLedgerAge();
/**
* @return Seconds between network time and the validation sign time of the
* last validated ledger; two weeks when nothing is validated.
*/
std::chrono::seconds
getValidatedLedgerAge();
/**
* Decides whether the node is close enough to the network to serve clients.
*
* @param reason Set to a human-readable explanation when the answer is false.
* @return false when nothing has been published, the published ledger is
* over three minutes old, or validation leads publication by over 90s.
*/
bool
isCaughtUp(std::string& reason);
/**
* Get the earliest ledger we will let peers fetch.
*
* @return fetchDepth_ behind the closed ledger, floored at zero.
* @note Requires a closed ledger to exist; do not call this before the
* first ledger has closed.
*/
std::uint32_t
getEarliestFetch();
/**
* Adds a ledger to the history cache. Does not itself validate the ledger,
* but honours the flag the ledger already carries: a ledger marked validated
* is also indexed by sequence.
*
* @param ledger Immutable ledger to cache.
* @return true if we already had the ledger.
*/
bool
storeLedger(std::shared_ptr<Ledger const> ledger);
/**
* A new ledger has been accepted as part of the trusted chain: mark it
* validated and full, record it as resident, persist it, and repair the
* chain behind it if the parent turns out to disagree.
*
* @param ledger Ledger to accept.
* @param isSynchronous true to write it to the database before returning.
* @param isCurrent true when the ledger is part of the live stream rather
* than back-filled history; only current ledgers enter the index.
*/
void
setFullLedger(std::shared_ptr<Ledger const> const& ledger, bool isSynchronous, bool isCurrent);
@@ -112,19 +270,48 @@ public:
* Check the sequence number and parent close time of a
* ledger against our clock and last validated ledger to
* see if it can be the network's current ledger
*
* @param ledger Candidate ledger; must not be null.
* @return false when the candidate precedes the validated ledger, its
* parent close time is over five minutes from network time, or its
* sequence runs further ahead than elapsed time could explain.
*/
bool
canBeCurrent(std::shared_ptr<Ledger const> const& ledger);
/**
* Adopts a ledger as the last closed ledger, then either accepts it
* directly (standalone) or tests it for validation (networked).
*
* @param lastClosed Closed, immutable ledger. Passing an open or mutable
* ledger is a logic error and terminates the process.
*/
void
switchLCL(std::shared_ptr<Ledger const> const& lastClosed);
/**
* Marks a ledger no longer resident after its save failed, and starts
* fetching it again.
*
* @param seq Sequence of the ledger that failed to save.
* @param hash Hash of that ledger, used to re-acquire it.
*/
void
failedSave(std::uint32_t seq, uint256 const& hash);
/**
* @return Resident ledger sequences as ranges, e.g. "3-8,10", or "empty".
*/
std::string
getCompleteLedgers() const;
/**
* Counts gaps in a closed sequence interval.
*
* @param first Lowest sequence to test; must not exceed last.
* @param last Highest sequence to test.
* @return How many sequences in [first, last] are not resident.
*/
std::size_t
missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const;
@@ -142,18 +329,30 @@ public:
* This is normally called when a transaction for that account is
* successfully applied to the open ledger so the next transaction
* can be resubmitted without waiting for ledger close.
*
* @param tx Transaction that was just applied; its account and sequence
* select the successor.
* @return The next held transaction for that account, or null if none.
*/
std::shared_ptr<STTx const>
popAcctTransaction(std::shared_ptr<STTx const> const& tx);
/**
* Get a ledger's hash by sequence number using the cache
*
* @param index Ledger sequence to look up.
* @return The hash, or zero when neither the cache nor the SQL index has it.
*/
uint256
getHashBySeq(std::uint32_t index);
/**
* Walk to a ledger's hash using the skip list
*
* @param index Ledger sequence wanted.
* @param reason Why the ledger is needed, for any acquire this triggers.
* @return The hash, or nullopt when there is no validated ledger to walk
* back from, or when the walk itself cannot produce it.
*/
std::optional<LedgerHash>
walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason);
@@ -165,6 +364,13 @@ public:
* precede the target index. This function may throw if nodes
* from the reference ledger or any prior ledger are not present
* in the node store.
*
* @param index Ledger sequence wanted.
* @param referenceLedger Ledger to walk back from.
* @param reason Why the ledger is needed, for any acquire this triggers.
* @return The hash, or nullopt when the reference ledger is null or
* precedes index, when the intervening hash page is missing, or when
* acquiring the intermediate ledger fails.
*/
std::optional<LedgerHash>
walkHashBySeq(
@@ -172,89 +378,294 @@ public:
std::shared_ptr<ReadView const> const& referenceLedger,
InboundLedger::Reason reason);
/**
* Finds a resident ledger by sequence, preferring the validated chain.
*
* @param index Ledger sequence wanted.
* @return The ledger, or null. A miss also drops index from the resident
* set, since we evidently do not have it.
*/
std::shared_ptr<Ledger const>
getLedgerBySeq(std::uint32_t index);
/**
* @param hash Ledger hash wanted.
* @return The ledger from the history cache or the closed ledger, or null.
*/
std::shared_ptr<Ledger const>
getLedgerByHash(uint256 const& hash);
/**
* Records a closed range of sequences as resident.
*
* @param minV Lowest sequence now present.
* @param maxV Highest sequence now present.
*/
void
setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV);
/**
* @param ledgerIndex Ledger sequence wanted.
* @return Its close time, or nullopt when the hash or the stored header
* cannot be found.
*/
std::optional<NetClock::time_point>
getCloseTimeBySeq(LedgerIndex ledgerIndex);
/**
* Reads a close time straight out of the serialized header in the node store.
*
* @param ledgerHash Hash of the ledger wanted.
* @param ledgerIndex Its sequence, used as the node-store lookup hint.
* @return Its close time, or nullopt when the object is absent, too short,
* or not a ledger header.
*/
std::optional<NetClock::time_point>
getCloseTimeByHash(LedgerHash const& ledgerHash, LedgerIndex ledgerIndex);
/**
* Defers a transaction to the next open ledger.
*
* @param trans Transaction to hold.
*/
void
addHeldTransaction(std::shared_ptr<Transaction> const& trans);
/**
* Walks back from a ledger dropping every resident ledger whose hash
* disagrees with that ledger's skip list, stopping at the first match.
*
* @param ledger Ledger whose chain is taken as correct.
*/
void
fixMismatch(ReadView const& ledger);
/**
* @param seq Ledger sequence to test.
* @return true when every node of that ledger is held locally.
*/
bool
haveLedger(std::uint32_t seq) const;
/**
* Marks a ledger no longer resident.
*
* @param seq Ledger sequence to drop.
*/
void
clearLedger(std::uint32_t seq);
/**
* Tests whether a ledger is on the validated chain, using the skip list
* when the ledger does not already say so.
*
* @param ledger Ledger to test; open ledgers are never validated.
* @return true when the ledger is part of the validated chain. A true
* answer is cached in the ledger header; a false one is recomputed on every
* call. When the SQL index names this ledger but the validated chain does
* not, the sequence is dropped from the resident set.
*/
bool
isValidated(ReadView const& ledger);
/**
* Returns Ledgers we have all the nodes for and are indexed: the fully
* validated range minus any sequence still being written.
*
* @param minVal Set to the lowest usable sequence, or 0 if none remains.
* @param maxVal Set to the highest usable sequence, or 0 if none remains.
* @return false when nothing has been published yet.
*/
bool
getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal);
/**
* Returns Ledgers we have all the nodes for: the contiguous resident range
* ending at the published ledger.
*
* @param minVal Set to the lowest sequence of that range.
* @param maxVal Set to the published sequence.
* @return false when nothing has been published yet.
*/
bool
getFullValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal);
/**
* Expires stale entries from the ledger history and fetch-pack caches.
*/
void
sweep();
/**
* @return Hit rate of the ledger-by-hash cache, as a percentage.
*/
float
getCacheHitRate();
/**
* Accepts a ledger as the new last fully validated ledger if it has a
* validation quorum, then republishes fees and pokes the stream forward.
*
* @param ledger Candidate ledger.
*/
void
checkAccept(std::shared_ptr<Ledger const> const& ledger);
/**
* Check if the specified ledger can become the new last fully-validated
* ledger, fetching it if it is not resident.
*
* @param hash Hash of the candidate ledger.
* @param seq Its sequence. Zero means the sequence is unknown, which skips
* the staleness checks.
*/
void
checkAccept(uint256 const& hash, std::uint32_t seq);
/**
* Report that the consensus process built a particular ledger
*
* Records the built ledger, then looks for the highest ledger with enough
* trusted validations to accept - which may not be this one.
*
* @param ledger Ledger consensus just built.
* @param consensusHash Hash of the consensus transaction set.
* @param consensus Consensus metadata for this round, kept only so a
* built-versus-validated mismatch can be logged.
*/
void
consensusBuilt(
std::shared_ptr<Ledger const> const& ledger,
uint256 const& consensusHash,
json::Value consensus);
/**
* Records which ledger consensus is currently building, so validations for
* it are not chased separately.
*
* @param index Sequence being built; 0 when none is.
*/
void
setBuildingLedger(LedgerIndex index);
/**
* Signals that the ledger stream may be able to make progress. Starts the
* advance job if it is not already running, and is cheap to call often.
*/
void
tryAdvance();
/**
* Notes a new pathfinding request and dispatches a worker if one is due.
*
* @return true if path request successfully placed.
*/
bool
newPathRequest(); // Returns true if path request successfully placed.
/**
* @return true if a new pathfinding request arrived since the last call.
* Reading the flag clears it.
*/
bool
isNewPathRequest();
/**
* If the order book is radically updated, we need to reprocess all
* pathfinding requests.
*
* @return true if able to fulfill request.
*/
bool
newOrderBookDB(); // Returns true if able to fulfill request.
/**
* Corrects the cached sequence-to-hash mapping for one ledger.
*
* @param ledgerIndex Sequence to correct.
* @param ledgerHash Hash that sequence really has.
* @return false when a different hash was cached, meaning the caller's
* view of history just changed; true when it already matched or was absent.
*/
bool
fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash);
/**
* Forgets that we hold any ledger below a sequence, without touching the
* cached ledgers themselves.
*
* @param seq First sequence to keep.
*/
void
clearPriorLedgers(LedgerIndex seq);
/**
* Drops cached ledgers below a sequence.
*
* @param seq First sequence to keep.
*/
void
clearLedgerCachePrior(LedgerIndex seq);
// ledger replay
/**
* Stores a transaction set to replay when the next ledger closes.
*
* @param replay Set to replay; replaces any set already held.
*/
void
takeReplay(std::unique_ptr<LedgerReplay> replay);
/**
* @return The stored replay set, transferring ownership; null if none.
*/
std::unique_ptr<LedgerReplay>
releaseReplay();
// Fetch Packs
/**
* Signals that fetch-pack data arrived, so waiting acquires can use it.
* At most one handler job is queued at a time.
*
* @param progress Unused by the current implementation.
* @param seq Unused by the current implementation.
*/
void
gotFetchPack(bool progress, std::uint32_t seq);
/**
* Caches one fetch-pack node received from a peer.
*
* @param hash Hash of the node, which is its key.
* @param data Serialized node.
*/
void
addFetchPack(uint256 const& hash, std::shared_ptr<Blob> data);
/**
* Consumes one cached fetch-pack node.
*
* @param hash Hash of the node wanted.
* @return The node, or nullopt when it is absent or its contents do not
* hash to the key. Either way the entry is removed, so a second call for
* the same hash returns nothing.
*/
std::optional<Blob>
getFetchPack(uint256 const& hash) override;
/**
* Builds a fetch pack of the ledgers preceding the one a peer says it has,
* and sends it. Silently declines when the node is too loaded, too far
* behind, or the request is over a second old.
*
* @param wPeer Peer to reply to; nothing is sent if it has gone away.
* @param request Originating request, whose ledger hash is echoed back.
* @param haveLedgerHash Newest ledger the peer claims to have. Its parent
* is the first one packed.
* @param uptime Time the request was received, used as the deadline base.
*/
void
makeFetchPack(
std::weak_ptr<Peer> const& wPeer,
@@ -262,6 +673,9 @@ public:
uint256 haveLedgerHash,
UptimeClock::time_point uptime);
/**
* @return Number of nodes currently held in the fetch-pack cache.
*/
std::size_t
getFetchPackCacheSize() const;
@@ -274,56 +688,162 @@ public:
return !validLedger_.empty();
}
// Returns the minimum ledger sequence in SQL database, if any.
/**
* Returns the minimum ledger sequence in SQL database, if any.
*
* @return That sequence, or nullopt when the database holds no ledgers.
*/
std::optional<LedgerIndex>
minSqlSeq();
// Iff a txn exists at the specified ledger and offset then return its txnid
/**
* Iff a txn exists at the specified ledger and offset then return its txnid
*
* @param ledgerSeq Ledger to search. Not a precondition: a sequence above
* the validated range yields nullopt rather than an error.
* @param txnIndex Position of the transaction within that ledger.
* @return The transaction ID, or nullopt when the ledger is unavailable or
* holds no transaction at that position.
*/
std::optional<uint256>
txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex);
private:
/**
* Adopts a ledger as the last validated one and tells the rest of the
* server, including the amendment-support checks that can block the node.
*
* @param l Ledger to adopt. Its recorded sign time is the median of the
* trusted validations, or its own close time when there are too few.
*/
void
setValidLedger(std::shared_ptr<Ledger const> const& l);
/**
* Records a ledger as the newest one published to clients.
*
* @param l Ledger just published.
*/
void
setPubLedger(std::shared_ptr<Ledger const> const& l);
/**
* Walks back from a ledger through the SQL index, marking each ancestor
* resident until the chain breaks or a known ledger is reached. Runs as a
* job, and clears fillInProgress_ when it finishes.
*
* @param ledger Ledger to walk back from.
*/
void
tryFill(std::shared_ptr<Ledger const> ledger);
/**
* Request a fetch pack to get to the specified ledger
*
* @param missing Sequence we lack; the request is keyed on its successor.
* @param reason Why the ledger is needed, for any hash walk this triggers.
*/
void
getFetchPack(LedgerIndex missing, InboundLedger::Reason reason);
/**
* Finds the hash of a ledger the history back-fill wants, preferring the
* last back-filled ledger as the reference to walk from.
*
* @param index Ledger sequence wanted.
* @param reason Why the ledger is needed, for any acquire this triggers.
* @return The hash, or nullopt if no reference ledger yields it.
*/
std::optional<LedgerHash>
getLedgerHashForHistory(LedgerIndex index, InboundLedger::Reason reason);
/**
* Determines how many validations are needed to fully validate a ledger
*
* @return Number of validations needed
*/
std::size_t
getNeededValidations();
/**
* Acquires one missing historical ledger, falling back to a fetch pack and
* then to prefetching a batch of its predecessors.
*
* @param missing Sequence to acquire.
* @param progress Set to true when history moved forward, and also when no
* hash could be found at all, so the caller stops retrying that sequence.
* @param reason Why the ledger is needed, passed to the acquire.
* @param sl Lock on mutex_, released for the duration of the network work.
*/
void
fetchForHistory(
std::uint32_t missing,
bool& progress,
InboundLedger::Reason reason,
std::unique_lock<std::recursive_mutex>&);
// Try to publish ledgers, acquire missing ledgers. Always called with
// mutex_ locked. The passed lock is a reminder to callers.
/**
* Try to publish ledgers, acquire missing ledgers. Always called with
* mutex_ locked. The passed lock is a reminder to callers.
*
* Publishing takes priority; history is only back-filled when the node is
* caught up, unloaded and idle. Loops until no progress is made.
*
* @param sl Lock on mutex_, released around publication and network work.
*/
void
doAdvance(std::unique_lock<std::recursive_mutex>&);
/**
* Collects the next run of validated ledgers to publish, acquiring or
* replaying the ones that are missing.
*
* @param sl Lock on mutex_, released while ledgers are fetched.
* @return Ledgers to publish in ascending order, empty when there is
* nothing to do. A gap of over 100 ledgers is skipped rather than filled,
* and the validated ledger alone is returned.
*/
std::vector<std::shared_ptr<Ledger const>>
findNewLedgersToPublish(std::unique_lock<std::recursive_mutex>&);
/**
* Runs pending pathfinding requests against the newest suitable ledger
* until none are left. Runs as a job, and decrements pathFindThread_ on
* each of its early returns - but not when a job-queue shutdown ends the
* loop, which leaves the count high for the rest of the process.
*/
void
updatePaths();
// Returns true if work started. Always called with mutex_ locked.
// The passed lock is a reminder to callers.
/**
* A thread needs to be dispatched to handle pathfinding work of some kind.
*
* Returns true if work started. Always called with mutex_ locked.
* The passed lock is a reminder to callers.
*
* @param name Job name, for the perf log.
* @param sl Lock on mutex_, held throughout.
* @return true when a pathfinding worker is running and the server is not
* shutting down, so the caller may expect its request to be serviced.
*/
bool
newPFWork(char const* name, std::unique_lock<std::recursive_mutex>&);
/**
* Owning application, the route to every other subsystem.
*/
Application& app_;
/**
* Log sink for this component.
*/
beast::Journal journal_;
/**
* Guards the tracked ledgers, the held transactions and the job
* bookkeeping below. Recursive because the advance and pathfinding paths
* re-enter public accessors. Taken before completeLock_.
*/
std::recursive_mutex mutable mutex_;
// The ledger that most recently closed.
@@ -344,14 +864,27 @@ private:
// Fully validated ledger, whether or not we have the ledger resident.
std::pair<uint256, LedgerIndex> lastValidLedger_{uint256(), 0};
/**
* Cache of ledgers by hash and of validated sequence-to-hash mappings.
*/
LedgerHistory ledgerHistory_;
/**
* Transactions deferred to the next open ledger, in canonical order.
*/
CanonicalTXSet heldTransactions_{uint256()};
// A set of transactions to replay during the next close
std::unique_ptr<LedgerReplay> replayData_;
/**
* Guards completeLedgers_ only. Always taken after mutex_.
*/
std::recursive_mutex mutable completeLock_;
/**
* Sequences of every ledger held locally in full.
*/
RangeSet<std::uint32_t> completeLedgers_;
// Publish thread is running.
@@ -359,17 +892,52 @@ private:
// Publish thread has work to do.
bool advanceWork_{false};
/**
* Sequence a tryFill() job is currently walking back from, or 0 when no
* fill is running. Keeps back-fill from competing with itself.
*/
int fillInProgress_{0};
/**
* Never allowed above two, so pathfinding cannot starve other jobs.
*/
int pathFindThread_{0}; // Pathfinder jobs dispatched
/**
* A pathfinding request arrived and has not been picked up yet.
*/
bool pathFindNewRequest_{false};
/**
* Set while a GotFetchPack job is outstanding, so only one ever is.
*/
std::atomic_flag gotFetchPackThread_ = ATOMIC_FLAG_INIT; // GotFetchPack jobs dispatched
/**
* Close time of pubLedger_, in seconds since the network epoch; 0 if none.
*/
std::atomic<std::uint32_t> pubLedgerClose_{0};
/**
* Sequence of pubLedger_; 0 if nothing has been published.
*/
std::atomic<LedgerIndex> pubLedgerSeq_{0};
/**
* Sign time of validLedger_, in seconds since the network epoch: the median
* of its trusted validations, or its close time when those were too few.
*/
std::atomic<std::uint32_t> validLedgerSign_{0};
/**
* Sequence of validLedger_; 0 if nothing is validated.
*/
std::atomic<LedgerIndex> validLedgerSeq_{0};
/**
* Sequence consensus is building; 0 when it is not building one.
*/
std::atomic<LedgerIndex> buildingLedgerSeq_{0};
// The server is in standalone mode
@@ -381,10 +949,19 @@ private:
// How much history do we want to keep
std::uint32_t const ledgerHistorySize_;
/**
* Cap on ledgers acquired in one publication or prefetch pass.
*/
std::uint32_t const ledgerFetchSize_;
/**
* Fetch-pack nodes keyed by node hash, expiring 45 seconds after use.
*/
TaggedCache<uint256, Blob> fetchPacks_;
/**
* Sequence a fetch pack was last requested for, to avoid asking twice.
*/
std::uint32_t fetchSeq_{0};
// Try to keep a validator from switching from test to live network
@@ -395,8 +972,18 @@ private:
TimeKeeper::time_point upgradeWarningPrevTime_;
private:
/**
* Ledger-age gauges reported to the insight collector. The collector calls
* the hook, which samples the two ages; nothing here is configurable.
*/
struct Stats
{
/**
* Registers the hook and the gauges.
*
* @param handler Callable the collector invokes to sample the gauges.
* @param collector Sink the gauges are reported to.
*/
template <class Handler>
Stats(Handler const& handler, beast::insight::Collector::ptr const& collector)
: hook(collector->makeHook(handler))
@@ -405,14 +992,32 @@ private:
{
}
/**
* Keeps the sampling callback registered for as long as Stats lives.
*/
beast::insight::Hook hook;
/**
* Age of the validated ledger, in seconds.
*/
beast::insight::Gauge validatedLedgerAge;
/**
* Age of the published ledger, in seconds.
*/
beast::insight::Gauge publishedLedgerAge;
};
/**
* The gauges and the collector hook that samples them.
*/
Stats stats_;
private:
/**
* Samples both ledger ages into the gauges. Called by the collector on its
* own thread, so it takes mutex_.
*/
void
collectMetrics()
{

View File

@@ -82,17 +82,39 @@
namespace xrpl {
// Don't catch up more than 100 ledgers (cannot exceed 256)
/**
* Don't catch up more than 100 ledgers (cannot exceed 256).
*
* A wider gap between the published and validated ledgers is abandoned rather
* than filled, and publication jumps straight to the validated ledger. The 256
* ceiling is how far back hashOfSeq() can reach in one skip-list lookup.
*/
static constexpr int kMaxLedgerGap{100};
// Don't acquire history if ledger is too old
/**
* Don't acquire history if ledger is too old. Age of the validated ledger,
* above which back-filling is skipped so the node can catch up first.
*/
static constexpr std::chrono::minutes kMaxLedgerAgeAcquire{1};
// Don't acquire history if write load is too high
/**
* Don't acquire history if write load is too high. Node-store write queue
* depth, in pending writes, above which back-filling is skipped.
*/
static constexpr int kMaxWriteLoadAcquire{8192};
// Helper function for LedgerMaster::doAdvance()
// Return true if candidateLedger should be fetched from the network.
/**
* Helper function for LedgerMaster::doAdvance()
* Return true if candidateLedger should be fetched from the network.
*
* @param currentLedger Sequence of the newest validated ledger.
* @param ledgerHistory How many ledgers of history the operator asked to keep.
* @param minimumOnline Lowest sequence the node must keep online, if known.
* @param candidateLedger Sequence of the missing ledger being considered.
* @param j Log sink for the decision.
* @return true when the candidate may be the current ledger, falls inside the
* configured history window, or is at or above the minimum to keep online.
*/
static bool
shouldAcquire(
std::uint32_t const currentLedger,
@@ -1126,12 +1148,27 @@ LedgerMaster::consensusBuilt(
auto validations =
app_.getValidators().negativeUNLFilter(app_.getValidations().currentTrusted());
// Track validation counts with sequence numbers
/**
* Track validation counts with sequence numbers.
*
* One tally per ledger hash, built while scanning the current trusted
* validations. A validation names a hash but not always a usable sequence,
* so the first sequence seen for a hash is kept and later ones ignored.
*
* @note Not thread-safe, and not intended to be: instances live only inside
* the loop below, which holds no lock.
*/
class ValSeq
{
public:
ValSeq() = default;
/**
* Counts one more validation for this ledger.
*
* @param seq Sequence the validation reported. Adopted only if no
* sequence is known yet; zero leaves the tally's sequence unknown.
*/
void
mergeValidation(LedgerIndex seq)
{
@@ -1142,7 +1179,14 @@ LedgerMaster::consensusBuilt(
ledgerSeq = seq;
}
/**
* How many trusted validations named this ledger.
*/
std::size_t valCount{0};
/**
* Sequence of this ledger, or 0 while still unknown.
*/
LedgerIndex ledgerSeq{0};
};