Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bcb9729
Determine TxQ network "health" by average consensus round time
ximinez Sep 29, 2023
97a93c3
[FOLD] Make the unhealthy factor a constexpr param, set to 3
ximinez Oct 3, 2023
c55848c
Include the current round time in the average round time computation
ximinez Oct 2, 2024
f8329f8
Determine TxQ network "health" by average consensus round time
ximinez Sep 29, 2023
9d689ac
[FOLD] Make the unhealthy factor a constexpr param, set to 3
ximinez Oct 3, 2023
e1c5ebf
Merge branch 'develop' into txq-duration
ximinez Nov 12, 2025
074d5e8
Merge branch 'develop' into txq-duration
ximinez Nov 13, 2025
bed1574
Merge branch 'develop' into txq-duration
ximinez Nov 15, 2025
e41652a
Merge branch 'develop' into txq-duration
ximinez Nov 19, 2025
6014d4f
Merge branch 'develop' into txq-duration
ximinez Nov 21, 2025
47fdcc4
Merge branch 'develop' into txq-duration
ximinez Nov 25, 2025
794246c
Merge branch 'develop' into txq-duration
ximinez Nov 28, 2025
448d89a
Merge branch 'develop' into txq-duration
ximinez Dec 1, 2025
d26cd6d
Merge branch 'develop' into txq-duration
ximinez Dec 3, 2025
f8c05ab
Merge branch 'develop' into txq-duration
ximinez Dec 6, 2025
cbde886
Merge branch 'develop' into txq-duration
ximinez Dec 13, 2025
de1996b
Merge branch 'develop' into txq-duration
ximinez Dec 19, 2025
01f27b7
Merge branch 'develop' into txq-duration
ximinez Dec 22, 2025
5fe6bb2
Merge branch 'develop' into txq-duration
ximinez Jan 6, 2026
28b9123
Merge branch 'develop' into txq-duration
ximinez Jan 8, 2026
363902d
Merge branch 'develop' into txq-duration
ximinez Jan 8, 2026
3c18125
Merge branch 'develop' into txq-duration
ximinez Jan 11, 2026
0a74a3f
Merge branch 'develop' into txq-duration
ximinez Jan 12, 2026
4aa213f
Merge branch 'develop' into txq-duration
ximinez Jan 13, 2026
43b0332
Merge branch 'develop' into txq-duration
ximinez Jan 15, 2026
ca6a2cc
Merge commit '92046785d1fea5f9efe5a770d636792ea6cab78b' into txq-dura…
ximinez Jan 28, 2026
61f9d60
Merge commit '5f638f55536def0d88b970d1018a465a238e55f4' into txq-dura…
ximinez Jan 28, 2026
3761137
Merge branch 'develop' into txq-duration
ximinez Jan 28, 2026
1a5dbd8
Fix formatting
ximinez Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/test/app/TxQ_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,9 +679,9 @@ class TxQPosNegFlows_test : public beast::unit_test::suite
env.fund(XRP(1000), noripple(alice, bob));
env.close(env.now() + 5s, 10000ms);
env.fund(XRP(1000), noripple(charlie, daria));
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 30001ms);
env.fund(XRP(1000), noripple(edgar, felicia));
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 60002ms);

checkMetrics(*this, env, 0, std::nullopt, 0, 2);
env(noop(bob));
Expand Down Expand Up @@ -796,7 +796,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite
env.fund(XRP(1000), noripple(alice, bob));
env.close(env.now() + 5s, 10000ms);
env.fund(XRP(1000), noripple(carol));
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 30001ms);

// Fill the ledger
env(noop(alice));
Expand Down Expand Up @@ -3636,19 +3636,19 @@ class TxQPosNegFlows_test : public beast::unit_test::suite
checkMetrics(*this, env, txCount, 56, 15, 14);

// Close the ledger with a delay.
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 30001ms);
txCount -= 8;
checkMetrics(*this, env, txCount, 56, 8, 7);

// Close the ledger with a delay.
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 60002ms);
txCount -= 4;
checkMetrics(*this, env, txCount, 56, 4, 3);

// From 28 expected back down to 3 in 3 "slow" ledgers.

// Confirm the minimum sticks
env.close(env.now() + 5s, 10000ms);
env.close(env.now() + 5s, 150s);
txCount -= 4;
checkMetrics(*this, env, txCount, 56, 4, 3);

Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/consensus/RCLConsensus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ RCLConsensus::Adaptor::buildLCL(

// Update fee computations based on accepted txs
using namespace std::chrono_literals;
app_.getTxQ().processClosedLedger(app_, *built, roundTime > 5s);
app_.getTxQ().processClosedLedger(app_, *built, roundTime);

// And stash the ledger in the ledger master
if (ledgerMaster_.storeLedger(built))
Expand Down
2 changes: 1 addition & 1 deletion src/xrpld/app/misc/NetworkOPs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@ NetworkOPsImp::switchLastClosedLedger(std::shared_ptr<Ledger const> const& newLC
clearNeedNetworkLedger();

// Update fee computations.
app_.getTxQ().processClosedLedger(app_, *newLCL, true);
app_.getTxQ().processClosedLedger(app_, *newLCL, std::nullopt);

// Caller must own master lock
{
Expand Down
35 changes: 30 additions & 5 deletions src/xrpld/app/misc/TxQ.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,18 @@ class TxQ
Any transactions for which the `LastLedgerSequence` has
passed are removed from the queue, and any account objects
that have no candidates under them are removed.

@param app Rippled Application object.
@param view View of the LCL that was just closed or received.
@param roundTime Time it took for the current consensus round to
complete. If unseated, indicates "unusual" processing, such as
startup or re-syncing.
*/
void
processClosedLedger(Application& app, ReadView const& view, bool timeLeap);
processClosedLedger(
Application& app,
ReadView const& view,
std::optional<std::chrono::milliseconds> const& roundTime);

/** Return the next sequence that would go in the TxQ for an account. */
SeqProxy
Expand Down Expand Up @@ -363,12 +372,19 @@ class TxQ
/// Recent history of transaction counts that
/// exceed the targetTxnCount_
boost::circular_buffer<std::size_t> recentTxnCounts_;
/// Recent history of consensus round times
boost::circular_buffer<std::chrono::milliseconds> recentRoundTimes_;
/// Based on the median fee of the LCL. Used
/// when fee escalation kicks in.
FeeLevel64 escalationMultiplier_;
/// Journal
beast::Journal const j_;

/// Any round time less than 5 seconds is considered good, regardless of
/// recent history.
static constexpr std::chrono::seconds timeLeapCutoff{5};
static constexpr std::uint32_t timeLeapFactor{3};

public:
/// Constructor
FeeMetrics(Setup const& setup, beast::Journal j)
Expand All @@ -380,6 +396,7 @@ class TxQ
: std::optional<std::size_t>(std::nullopt))
, txnsExpected_(minimumTxnCount_)
, recentTxnCounts_(setup.ledgersInQueue)
, recentRoundTimes_(setup.ledgersInQueue)
, escalationMultiplier_(setup.minimumEscalationMultiplier)
, j_(j)
{
Expand All @@ -391,12 +408,20 @@ class TxQ

@param app Rippled Application object.
@param view View of the LCL that was just closed or received.
@param timeLeap Indicates that rippled is under load so fees
should grow faster.
@param roundTime Time it took for the current consensus round to
complete. If unseated, indicates "unusual" processing, such as
startup or re-syncing.
@param setup Customization params.

@return bool indicating whether the round time was unusually high,
i.e. a "time leap".
*/
std::size_t
update(Application& app, ReadView const& view, bool timeLeap, TxQ::Setup const& setup);
[[nodiscard]] bool
update(
Application& app,
ReadView const& view,
std::optional<std::chrono::milliseconds> const& roundTime,
TxQ::Setup const& setup);

/// Snapshot of the externally relevant FeeMetrics
/// fields at any given time.
Expand Down
31 changes: 25 additions & 6 deletions src/xrpld/app/misc/detail/TxQ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ increase(FeeLevel64 level, std::uint32_t increasePercent)

//////////////////////////////////////////////////////////////////////////

std::size_t
TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, TxQ::Setup const& setup)
bool
TxQ::FeeMetrics::update(
Application& app,
ReadView const& view,
std::optional<std::chrono::milliseconds> const& roundTime,
TxQ::Setup const& setup)
{
std::vector<FeeLevel64> feeLevels;
auto const txBegin = view.txs.begin();
Expand All @@ -72,9 +76,21 @@ TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, T
std::sort(feeLevels.begin(), feeLevels.end());
XRPL_ASSERT(size == feeLevels.size(), "xrpl::TxQ::FeeMetrics::update : fee levels size");

using namespace std::chrono;

milliseconds const averageTime = recentRoundTimes_.empty()
? 0ms
: milliseconds{
std::accumulate(recentRoundTimes_.begin(), recentRoundTimes_.end(), 0ms) / recentRoundTimes_.size()};
bool const timeLeap = !roundTime || (roundTime > timeLeapCutoff && roundTime > averageTime * timeLeapFactor);
if (roundTime)
recentRoundTimes_.push_back(*roundTime);

JLOG((timeLeap ? j_.warn() : j_.debug()))
<< "Ledger " << view.header().seq << " has " << size << " transactions. "
<< "Ledgers are processing " << (timeLeap ? "slowly" : "as expected") << ". Expected transactions is currently "
<< "Ledgers are processing " << (timeLeap ? "slowly" : "as expected") << ". Current consensus round took "
<< (roundTime ? to_string(roundTime->count()) + "ms" : "INDETERMINATE TIME")
<< " and recent average round time is " << averageTime.count() << "ms. Expected transactions is currently "
<< txnsExpected_ << " and multiplier is " << escalationMultiplier_;

if (timeLeap)
Expand Down Expand Up @@ -129,7 +145,7 @@ TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, T
JLOG(j_.debug()) << "Expected transactions updated to " << txnsExpected_ << " and multiplier updated to "
<< escalationMultiplier_;

return size;
return timeLeap;
}

FeeLevel64
Expand Down Expand Up @@ -1233,11 +1249,14 @@ TxQ::apply(Application& app, OpenView& view, std::shared_ptr<STTx const> const&

*/
void
TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap)
TxQ::processClosedLedger(
Application& app,
ReadView const& view,
std::optional<std::chrono::milliseconds> const& roundTime)
{
std::lock_guard lock(mutex_);

feeMetrics_.update(app, view, timeLeap, setup_);
bool const timeLeap = feeMetrics_.update(app, view, roundTime, setup_);
auto const& snapshot = feeMetrics_.getSnapshot();

auto ledgerSeq = view.header().seq;
Expand Down