From 793d2ecfcec75b6e55c7982f6e8bbeea367222d6 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:06:33 +0100 Subject: [PATCH] feat(telemetry): add txq expired/dropped counters for queue backpressure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The transaction queue had no metric for demand that leaves or never enters the queue, so fee-underpayment abandonment and admission-control rejection were invisible (distinct from jq_trans_overflow, which is the job queue). Add two synchronous counters via MetricsRegistry: - xrpld_txq_expired_total — incremented in TxQ::processClosedLedger() for each queued transaction removed because its LastLedgerSequence passed (submitters who under-bid the escalating fee and were never included). - xrpld_txq_dropped_total{reason} — incremented in TxQ::apply() at the queue-full admission-control returns (reason="queue_full"). Both reach MetricsRegistry via the Application& parameter already passed to these methods; calls are null-guarded so they no-op when telemetry is disabled. Co-Authored-By: Claude Opus 4.8 --- src/xrpld/app/misc/detail/TxQ.cpp | 10 ++++++++++ src/xrpld/telemetry/MetricsRegistry.cpp | 22 ++++++++++++++++++++++ src/xrpld/telemetry/MetricsRegistry.h | 23 +++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 352bef6bd9..ad14d6ae5f 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -1254,6 +1255,8 @@ TxQ::apply( JLOG(j_.info()) << "Queue is full, and transaction " << transactionID << " would kick a transaction from the same account (" << account << ") out of the queue."; + if (auto* const metrics = app.getMetricsRegistry(); metrics != nullptr) + metrics->incrementTxqDropped("queue_full"); return {telCAN_NOT_QUEUE_FULL, false}; } auto const& endAccount = byAccount_.at(lastRIter->account); @@ -1297,6 +1300,8 @@ TxQ::apply( { JLOG(j_.info()) << "Queue is full, and transaction " << transactionID << " fee is lower than end item's account average fee"; + if (auto* const metrics = app.getMetricsRegistry(); metrics != nullptr) + metrics->incrementTxqDropped("queue_full"); return {telCAN_NOT_QUEUE_FULL, false}; } } @@ -1366,12 +1371,17 @@ TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap) maxSize_ = std::max(snapshot.txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin); // Remove any queued candidates whose LastLedgerSequence has gone by. + auto* const metrics = app.getMetricsRegistry(); for (auto candidateIter = byFee_.begin(); candidateIter != byFee_.end();) { if (candidateIter->lastValid && *candidateIter->lastValid <= ledgerSeq) { byAccount_.at(candidateIter->account).dropPenalty = true; candidateIter = erase(candidateIter); + // Count each expired transaction: submitters who under-bid the + // escalating fee and were never included before expiry. + if (metrics != nullptr) + metrics->incrementTxqExpired(); } else { diff --git a/src/xrpld/telemetry/MetricsRegistry.cpp b/src/xrpld/telemetry/MetricsRegistry.cpp index ef1c4ead47..ea3553f12d 100644 --- a/src/xrpld/telemetry/MetricsRegistry.cpp +++ b/src/xrpld/telemetry/MetricsRegistry.cpp @@ -240,6 +240,10 @@ MetricsRegistry::start(std::string const& endpoint, std::string const& instanceI ledgerHistoryMismatchCounter_ = meter_->CreateUInt64Counter( "xrpld_ledger_history_mismatch_total", "Total built-vs-validated ledger mismatches by reason"); + txqExpiredCounter_ = meter_->CreateUInt64Counter( + "xrpld_txq_expired_total", "Total transactions expired out of the transaction queue"); + txqDroppedCounter_ = meter_->CreateUInt64Counter( + "xrpld_txq_dropped_total", "Total transactions refused admission to the queue by reason"); validationAgreementsCounter_ = meter_->CreateUInt64Counter( "xrpld_validation_agreements_total", "Total validation agreements"); validationMissedCounter_ = @@ -1338,4 +1342,22 @@ MetricsRegistry::incrementLedgerHistoryMismatch(std::string_view reason) #endif } +void +MetricsRegistry::incrementTxqExpired() +{ +#ifdef XRPL_ENABLE_TELEMETRY + if (enabled_ && txqExpiredCounter_) + txqExpiredCounter_->Add(1); +#endif +} + +void +MetricsRegistry::incrementTxqDropped(std::string_view reason) +{ +#ifdef XRPL_ENABLE_TELEMETRY + if (enabled_ && txqDroppedCounter_) + txqDroppedCounter_->Add(1, {{"reason", std::string(reason)}}); +#endif +} + } // namespace xrpl::telemetry diff --git a/src/xrpld/telemetry/MetricsRegistry.h b/src/xrpld/telemetry/MetricsRegistry.h index 8ae9129758..be623e4c53 100644 --- a/src/xrpld/telemetry/MetricsRegistry.h +++ b/src/xrpld/telemetry/MetricsRegistry.h @@ -360,6 +360,23 @@ public: void incrementLedgerHistoryMismatch(std::string_view reason); + /** Increment the txq_expired_total counter. + Called from TxQ::processClosedLedger() for each queued transaction + removed because its LastLedgerSequence has passed — submitters who + under-bid the escalating fee and were never included. + */ + void + incrementTxqExpired(); + + /** Increment the txq_dropped_total{reason} counter. + Called from TxQ::apply() when a transaction is refused admission to + the queue (e.g. the queue is full). Distinct from expiry (already + queued) and from jq_trans_overflow (job queue, not TxQ). + @param reason Admission-control rejection cause (e.g. "queue_full"). + */ + void + incrementTxqDropped(std::string_view reason); + /** Access the validation agreement tracker. Used by consensus and ledger hooks to record our validations and network validations so the tracker can compute agreement percentages. @@ -498,6 +515,12 @@ private: /// built-vs-validated ledger mismatch. opentelemetry::nostd::unique_ptr> ledgerHistoryMismatchCounter_; + /// Counter: xrpld_txq_expired_total — incremented per transaction expired out of the + /// transaction queue. + opentelemetry::nostd::unique_ptr> txqExpiredCounter_; + /// Counter: xrpld_txq_dropped_total{reason} — incremented when a transaction is refused + /// admission to the queue. + opentelemetry::nostd::unique_ptr> txqDroppedCounter_; /// Counter: xrpld_validation_agreements_total — incremented by ValidationTracker on /// agreement. opentelemetry::nostd::unique_ptr>