diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index 48f73d6781..9465eb480d 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -194,6 +194,10 @@ private: /// with the outcome (complete/failed), timeout count, and peer count. /// Gives operators visibility into back-fill / fork-recovery cost, which /// previously emitted no span or metric. + /// Stored detached: emplaced by the acquiring thread, reset on a + /// JtLedgerData worker. detached() strips the thread-local Scope so the + /// guard can be destroyed on the worker without corrupting the origin + /// thread's context stack. std::optional acquireSpan_; }; diff --git a/src/xrpld/app/ledger/detail/InboundLedger.cpp b/src/xrpld/app/ledger/detail/InboundLedger.cpp index 45c2d4d462..1f7283233b 100644 --- a/src/xrpld/app/ledger/detail/InboundLedger.cpp +++ b/src/xrpld/app/ledger/detail/InboundLedger.cpp @@ -103,8 +103,12 @@ InboundLedger::init(ScopedLockType& collectionLock) // observable. Finalized in done() with the outcome and timeout count. { using namespace telemetry; + // Detach the Scope: this guard is emplaced here but reset() on a + // JtLedgerData worker thread. Detaching strips the thread-local Scope + // so its destruction there does not corrupt this thread's context stack. acquireSpan_.emplace( - SpanGuard::span(TraceCategory::Ledger, seg::ledger, ledger_span::op::acquire)); + SpanGuard::span(TraceCategory::Ledger, seg::ledger, ledger_span::op::acquire) + .detached()); if (*acquireSpan_) { acquireSpan_->setAttribute(ledger_span::attr::ledgerSeq, static_cast(seq_));