Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing

Brings review fixes: no-alloc GetCurrent hot path, activateIfLive helper,
non-copyable storage, accurate drop-counter doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pratik Mankawde
2026-07-23 13:59:05 +01:00
4 changed files with 65 additions and 2 deletions

View File

@@ -86,6 +86,17 @@ class CoroAwareContextStorage : public opentelemetry::context::RuntimeContextSto
public:
CoroAwareContextStorage() = default;
// Non-copyable and non-movable: the LocalValue store is keyed by the
// address of stack_, so copying or moving would mis-key the store and
// strand its entries. Only ever new-ed once and installed on the SDK, so
// these are for intent/safety rather than a live bug.
CoroAwareContextStorage(CoroAwareContextStorage const&) = delete;
CoroAwareContextStorage&
operator=(CoroAwareContextStorage const&) = delete;
CoroAwareContextStorage(CoroAwareContextStorage&&) = delete;
CoroAwareContextStorage&
operator=(CoroAwareContextStorage&&) = delete;
/**
* @return the current (top-of-stack) context for this coro/thread.
*/

View File

@@ -929,6 +929,24 @@ public:
operator=(ScopedActivation const&) = delete;
};
/**
* Activate a span guard as the ambient context if it is live, else return a
* no-op activation. Accepts any handle (shared_ptr / optional) that is
* contextually convertible to bool and dereferences to a SpanGuard. Non-owning:
* the handle still owns/ends the span. Returns a null ScopedActivation when the
* handle is empty or the span is inactive (e.g. telemetry disabled).
* @param guard A shared_ptr<SpanGuard> or optional<SpanGuard>.
* @return An RAII activation; drop it to restore the prior context.
*/
template <class SpanGuardHandle>
[[nodiscard]] ScopedActivation
activateIfLive(SpanGuardHandle const& guard)
{
if (guard && *guard)
return guard->activate();
return ScopedActivation{};
}
// ---------------------------------------------------------------------------
// No-op stub (all inline, zero overhead, no OTel dependency)
// ---------------------------------------------------------------------------
@@ -1215,6 +1233,22 @@ public:
}
};
/**
* No-op counterpart to the telemetry-enabled activateIfLive(). Same signature
* and handle contract (shared_ptr / optional of SpanGuard); always yields a
* null ScopedActivation because a stub guard is never live.
* @param guard A shared_ptr<SpanGuard> or optional<SpanGuard>.
* @return A null (no-op) ScopedActivation.
*/
template <class SpanGuardHandle>
[[nodiscard]] ScopedActivation
activateIfLive(SpanGuardHandle const& guard)
{
if (guard && *guard)
return guard->activate();
return ScopedActivation{};
}
#endif // XRPL_ENABLE_TELEMETRY
} // namespace xrpl::telemetry

View File

@@ -12,6 +12,8 @@
#include <xrpl/telemetry/CoroAwareContextStorage.h>
#include <xrpl/basics/LocalValue.h>
#include <opentelemetry/context/context.h>
#include <opentelemetry/context/runtime_context.h>
#include <opentelemetry/nostd/unique_ptr.h>
@@ -21,6 +23,20 @@ namespace xrpl::telemetry {
opentelemetry::context::Context
CoroAwareContextStorage::GetCurrent() noexcept
{
// Hot path: Log.cpp reads the current context on EVERY log line on EVERY
// thread. Peek the raw per-thread/coro store WITHOUT materializing it:
// getLocalValues().get() returns nullptr when this thread has no store yet
// and never allocates. Dereferencing stack_ (a LocalValue) would instead
// heap-allocate the store + a map entry + the vector on first touch, so we
// avoid that on the no-span path.
//
// Behaviour is identical to touching *stack_: a thread with no store has no
// active span, and a store whose stack was never pushed yields an empty
// vector — both cases map to an empty Context, matching stock
// ThreadLocalContextStorage (Top() returns Context() when the stack is
// empty). Only when a store already exists do we read the top frame.
if (detail::getLocalValues().get() == nullptr)
return opentelemetry::context::Context{};
auto& stack = *stack_;
return stack.empty() ? opentelemetry::context::Context{} : stack.back();
}

View File

@@ -96,8 +96,10 @@ PendingTraceId::~PendingTraceId() noexcept
if (!gTlsPendingConsumed)
{
// The assert above is stripped in release, so bump a counter too: a
// dropped deterministic trace root stays observable via
// unconsumedDeterministicIdDrops(). Relaxed: this is a diagnostic tally.
// process-wide tally of dropped deterministic trace roots. It is
// exposed via unconsumedDeterministicIdDrops() as an extension point
// for a future metric or diagnostic to read; nothing wires it to a log
// or metric yet. Relaxed: this is a plain diagnostic tally.
gUnconsumedDeterministicIdDrops.fetch_add(1, std::memory_order_relaxed);
}
gTlsPendingTraceId.reset(); // never leak, even in release