From f1df5de8ddf362c2791cae7c0fc78c4755a07b48 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:00:31 +0100 Subject: [PATCH] feat(telemetry): count relayed transactions this node did not process Moving the tx.receive span past the duplicate check removed the only record of why a relayed copy was dropped. A labelled counter restores it, and covers the inner-batch reject the span never usefully reached. The count itself was never span-derived: transactions_duplicate already totals the dropped copies and predates the telemetry work. What was missing is the split between ordinary relay overlap and a peer sending traffic it should not. A counter also survives sampling and runs with tracing off, which the span attribute did not. --- include/xrpl/telemetry/MetricNames.h | 20 ++++++++++++++++++++ src/xrpld/overlay/detail/PeerImp.cpp | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/xrpl/telemetry/MetricNames.h b/include/xrpl/telemetry/MetricNames.h index 0de6b1e8fa..bfc3abe0f7 100644 --- a/include/xrpl/telemetry/MetricNames.h +++ b/include/xrpl/telemetry/MetricNames.h @@ -253,6 +253,10 @@ inline constexpr char peerAcceptTotal[] = "peer_accept_total"; * Peer disconnects, by cause and connection direction. */ inline constexpr char peerDisconnectTotal[] = "peer_disconnect_total"; +/** + * Relayed transactions this node did not process, by reason. + */ +inline constexpr char peerTxRejectedTotal[] = "peer_tx_rejected_total"; /** * Peer data requests this node declined to serve, by kind and cause. */ @@ -762,6 +766,22 @@ inline constexpr char graceful[] = "graceful"; inline constexpr char readError[] = "read_error"; } // namespace disconnect +/** + * `peer_tx_rejected_total` reasons -- why a relayed transaction was not + * processed. + * + * `duplicate` is ordinary relay overlap and dominates, because a peer relays + * every transaction it hears. `known_bad` and `inner_batch` mean a peer sent + * traffic it should not, so the split is what separates normal overlap from a + * misbehaving peer. The total across reasons matches the + * `transactions_duplicate` traffic category for the first two. + */ +namespace tx_rejected { +inline constexpr char duplicate[] = "duplicate"; +inline constexpr char knownBad[] = "known_bad"; +inline constexpr char innerBatch[] = "inner_batch"; +} // namespace tx_rejected + /** * `serve_refused_total` request kinds: what the peer had asked for. */ diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 59015bec2e..72d60ec257 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -1428,6 +1428,12 @@ PeerImp::handleTransaction( */ if (stx->isFlag(tfInnerBatchTxn)) { + XRPL_METRIC_COUNTER_INC_LABELED( + app_, + telemetry::metric::peerTxRejectedTotal, + "Relayed transactions not processed, by reason", + {{telemetry::label::reason, + std::string(telemetry::lval::tx_rejected::innerBatch)}}); JLOG(pJournal_.warn()) << "Ignoring Network relayed Tx containing " "tfInnerBatchTxn (handleTransaction)."; fee_.update(resource::kFeeModerateBurdenPeer, "inner batch txn"); @@ -1443,11 +1449,24 @@ PeerImp::handleTransaction( // we have seen this transaction recently if (any(flags & HashRouterFlags::BAD)) { + XRPL_METRIC_COUNTER_INC_LABELED( + app_, + telemetry::metric::peerTxRejectedTotal, + "Relayed transactions not processed, by reason", + {{telemetry::label::reason, + std::string(telemetry::lval::tx_rejected::knownBad)}}); fee_.update(resource::kFeeUselessData, "known bad"); JLOG(pJournal_.debug()) << "Ignoring known bad tx " << txID; } else { + XRPL_METRIC_COUNTER_INC_LABELED( + app_, + telemetry::metric::peerTxRejectedTotal, + "Relayed transactions not processed, by reason", + {{telemetry::label::reason, + std::string(telemetry::lval::tx_rejected::duplicate)}}); + // Erase only if the server has seen this tx. If the server // has not seen this tx then the tx could not have been // queued for this peer.