feat(telemetry): join ledger.acquire into the per-ledger trace

Completes the join left open when the acquire and consensus work landed in
parallel. The acquire span now derives its trace id from the ledger hash it
already records, so a fetch shares one trace with that ledger's validation,
acceptance and store, and its three phase children inherit the same id.

Reading one trace now answers the whole question for a slow ledger: whether
the data was slow to arrive, slow to be accepted, or slow to persist.

The acquire span stays an optional member of the join group, for the reason
its own entry already gives: a healthy cluster agreeing from genesis rarely
back-fills, so the span need not appear on every run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-07-25 20:26:40 +01:00
parent 41b818b55b
commit 0aa76527db
2 changed files with 15 additions and 2 deletions

View File

@@ -34,7 +34,6 @@
#include <xrpl/shamap/SHAMapNodeID.h>
#include <xrpl/shamap/SHAMapSyncFilter.h>
#include <xrpl/telemetry/SpanGuard.h>
#include <xrpl/telemetry/SpanNames.h>
#include <boost/iterator/function_output_iterator.hpp>
@@ -111,8 +110,15 @@ InboundLedger::init(ScopedLockType& collectionLock)
// acquireSpan_ is emplaced here but reset() on a JtLedgerData worker
// thread. A SpanGuard is thread-free (owns no thread-local Scope), so it
// can be created here and destroyed on the worker with no scope to strip.
// hashSpan, not span: the trace id is derived from hash_[0:16], so this
// acquire lands in the same trace as the ledger.validate,
// ledger.store and consensus.validation.accept spans for the same
// ledger, which run on other threads. One trace then shows whether a
// slow ledger was slow to fetch, to be accepted, or to be stored. The
// phase children below inherit this trace id automatically.
acquireSpan_.emplace(
SpanGuard::span(TraceCategory::Ledger, seg::ledger, ledger_span::op::acquire));
SpanGuard::hashSpan(
TraceCategory::Ledger, ledger_span::acquireFull, hash_.data(), hash_.kBytes));
if (*acquireSpan_)
{
// The hash is the one identity known at every acquire's start (a

View File

@@ -65,6 +65,13 @@ inline constexpr auto txset = makeStr("txset");
* names a dashboard and TraceQL query match on, so they must stay stable and
* unambiguous rather than reading as a further-nested `as.tree`.
*/
/**
* The parent acquire span's full name. Named here rather than composed at the
* call site because it is passed to hashSpan(), which takes one complete name,
* and because the phase names below are built from the same two segments.
*/
inline constexpr auto acquireFull = join(seg::ledger, op::acquire);
inline constexpr auto acquireHeader = join(join(seg::ledger, op::acquire), makeStr("header"));
inline constexpr auto acquireAsTree = join(join(seg::ledger, op::acquire), makeStr("astree"));
inline constexpr auto acquireTxTree = join(join(seg::ledger, op::acquire), makeStr("txtree"));