fix(telemetry): detach InboundLedger acquire span (created and reset on different threads)

acquireSpan_ is emplaced on the acquiring caller thread but reset() in
done() on a JtLedgerData worker. Detaching strips the thread-local OTel
Scope on the origin thread so destruction on the worker does not corrupt
the origin thread's context stack.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-07-17 22:26:39 +01:00
parent eb698a312a
commit 71f0aa03a8
2 changed files with 9 additions and 1 deletions

View File

@@ -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<telemetry::SpanGuard> acquireSpan_;
};

View File

@@ -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<int64_t>(seq_));