Commit Graph

113 Commits

Author SHA1 Message Date
Pratik Mankawde
caa704de10 merge: bring the review fixes forward from phase1c-rpc-integration
Two conflicts, both resolved by composing the sides rather than taking one.

cfg/xrpld-example.cfg: this branch had moved the batch-processor keys under
their own heading while upstream edited them in place, so a merge-both would
have documented them twice. Upstream's range sentences are applied to the
relocated block and the head-sampling note keeps its position.

02-design-decisions.md: the summary table changed on both sides for different
reasons. Upstream renamed ledger_index to current_ledger_seq and ledger_seq;
this branch had corrected the PathFinding row to the keys it actually emits.
Both are kept.
2026-09-07 15:00:53 +01:00
Pratik Mankawde
a8d678f354 merge: bring the childSpan pseudocode fix forward from phase1b-telemetry-infra 2026-09-07 14:56:32 +01:00
Pratik Mankawde
0f49aecbf0 docs(telemetry): pass a full dotted constant in the childSpan pseudocode
childSpan() takes the span name verbatim, so a bare op:: suffix names the span
"process" rather than "rpc.process". The same defect was corrected in the
SpanGuard and Telemetry examples; this is the last copy.
2026-09-07 14:56:29 +01:00
Pratik Mankawde
a6c24848ba merge: bring the review fixes forward from phase1b-telemetry-infra 2026-09-07 14:55:22 +01:00
Pratik Mankawde
18abd100b5 feat(telemetry): let a call site choose a span's role, not just its category
Span kind was derived from TraceCategory alone, so every Rpc-category span was
kServer. A category cannot tell an inbound handler from the internal work under
it, and trace backends pair kServer with kClient, so internal spans left as
kServer become unpaired edges in a service graph and read as extra inbound
requests.

SpanRole is a new xrpl-owned enum, orthogonal to TraceCategory: the category
names the subsystem and gates the span on config, the role says whether the span
handles a remote call. It is a defaulted fourth parameter on span(), freshRoot()
and the ScopedSpanGuard equivalents, defaulting to SpanRole::FromCategory, so no
existing call site changes. resolveSpanKind() applies an explicit role and falls
back to the category map, which keeps its single responsibility. The
telemetry-disabled stubs mirror all four signatures.

No call site passes a role yet. The two that need it are on a later branch.

Also fixes a ScopedSpanGuard example that passed a bare op:: suffix to
childSpan(), which takes the name verbatim. Naming the child rpc.command made it
a child of rpc.command.<cmd>, inverting the hierarchy, so the example's parent is
now rpc.process and the command attribute moved onto the command span.
2026-09-07 13:39:57 +01:00
Pratik Mankawde
f8e0a19b9f fix(telemetry): correct the childSpan doc examples and make Rule D tests real
Review feedback on the RPC integration PR.

The childSpan examples could not work as written. childSpan() takes its parent
from the ambient context and uses impl_ only as a liveness gate, so an unscoped
SpanGuard parent produced two siblings rather than a parent and child. The parent
is now a ScopedSpanGuard, the child no longer reuses the parent's name, and the
examples pass a full dotted constant because childSpan() takes the name verbatim.

Five of the ten Rule D tests could not fail. Four passed an empty L1 key set,
which makes the rule skip validation altogether; the fifth asserted an empty
result against an escaped-quote selector that extracted no labels at all. Each
now passes a nonempty L1 set and carries a known-bad label in the same
expression, so it asserts both that the intended labels are accepted and that
Rule D ran. Verified by disabling the rule: the old tests stay green, the new
ones all fail.

Span kind is not fixed here. categoryToSpanKind and the span factories belong to
the telemetry library, so the role parameter is routed to that branch, and the
two call sites here follow once it exists.
2026-09-07 13:25:22 +01:00
Pratik Mankawde
4baac61dd9 merge: bring the traces_endpoint rename forward from phase-1c 2026-09-03 15:14:20 +01:00
Pratik Mankawde
499984681c merge: bring the traces_endpoint rename forward from phase-1b 2026-09-03 15:13:38 +01:00
Pratik Mankawde
f68cf0d009 refactor(telemetry): name the traces endpoint for its signal
[telemetry] endpoint carried one OTLP signal while its name implied it
covered every signal. That asymmetry is what let the metrics URL be
guessed later by rewriting this one's path suffix, so anything not
ending /v1/traces silently posted metrics to the traces path.

Renames the key to traces_endpoint and Setup::exporterEndpoint to
tracesEndpoint. The default value is unchanged and the URL is still used
verbatim, with no path derived from it. The startup log line and the
compose-file example name the new key, the latter being where an
operator copies it from.

No metrics_endpoint is added here: this branch has no metrics pipeline,
so the key would parse into a member nothing reads.
2026-09-03 15:09:19 +01:00
Pratik Mankawde
daf9822eab Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-09-03 15:07:55 +01:00
Pratik Mankawde
d2cb0eb03c Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-09-03 15:07:25 +01:00
Pratik Mankawde
9d71dea972 refactor(telemetry): mark the telemetry accessors nodiscard
getInstance(), getTracer(), both startSpan() overloads and networkTypeFromId()
return values that a caller must use. A discarded startSpan() result destroys
the returned span immediately, so the span opens and closes with no content.

Six methods in Telemetry.h already carried the attribute, on the base and on
every override. The new attributes follow that: the overrides in Telemetry.cpp
and NullTelemetry.cpp get it too, because [[nodiscard]] is not inherited and a
call bound to the derived type would otherwise be unchecked.

No caller anywhere in the chain discards any of these results.
2026-09-03 15:00:28 +01:00
Pratik Mankawde
488617cc0b refactor(telemetry): mark the unused setter parameter [[maybe_unused]]
The default setServiceInstanceId() body ignores its argument. Use the
attribute rather than a (void) cast: the codebase already uses it 104 times
and the build is C++23.
2026-09-03 10:51:47 +01:00
Pratik Mankawde
2ea9005c1b Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-08-27 12:08:59 +01:00
Pratik Mankawde
69c413d332 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-08-27 12:08:26 +01:00
Pratik Mankawde
05d500d755 fix(telemetry): guard the includes only the telemetry build uses
With telemetry compiled out, <string_view> in Telemetry.h and <memory> in
SpanGuard.h are named only by declarations that are themselves guarded, so
clang-tidy's include-cleaner reports them unused and warnings-as-errors fails
the build. NullTelemetry.cpp has the mirror problem: it names beast::Journal
only in the compiled-out makeTelemetry(), reaching the type transitively.

Guard each include to match the configuration that uses it, and correct three
comments that overstated what the empty destructors cost.
2026-08-26 18:07:12 +01:00
Pratik Mankawde
095a702fbe fix(telemetry): keep the compiled-out span guards non-trivially destructible
The compiled-out ScopedActivation, SpanGuard and ScopedSpanGuard each used a
defaulted destructor. A defaulted destructor on an empty class is trivial, so
compilers report any guard held only for its scope as an unused variable. With
telemetry compiled out that produced seven -Wunused-variable errors under
-Werror, across the ledger acquire, consensus, ledger master and overlay paths.

Write the three destructors by hand so destruction is not trivial, matching the
telemetry-enabled types, and assert that property so it cannot quietly regress
to `= default`. The bodies are empty, so no code is generated either way.

Also move <memory> inside the telemetry guard: only the telemetry-enabled types
hold a unique_ptr, so the include is unused when telemetry is compiled out.
2026-08-26 15:46:06 +01:00
Pratik Mankawde
6165a26fed Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-24 12:25:36 +01:00
Pratik Mankawde
0feb356d42 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-24 12:25:35 +01:00
Pratik Mankawde
017c82eeee make span functions noexcept
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
2026-07-24 12:20:35 +01:00
Pratik Mankawde
842f994dbb Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-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>
2026-07-23 13:59:01 +01:00
Pratik Mankawde
be78519581 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
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>
2026-07-23 13:58:58 +01:00
Pratik Mankawde
c209829c44 refactor(telemetry): 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>
2026-07-23 13:50:16 +01:00
Pratik Mankawde
45cf7cccd0 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing
Brings M1 (dropped-deterministic-id counter) + M2 (childSpan doc) forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 23:14:39 +01:00
Pratik Mankawde
fe0bfe5147 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
Brings M1 (release-visible dropped-deterministic-id counter) + M2 (childSpan doc) forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 23:14:22 +01:00
Pratik Mankawde
8c86e01ace feat(telemetry): release-visible counter for dropped deterministic trace_id; clarify childSpan(name) doc
M1: ~PendingTraceId now increments a process-wide atomic counter when a
pinned deterministic trace_id is destroyed unconsumed, so the silent drop
stays observable in release builds where the existing XRPL_ASSERT is a
no-op. Exposed via unconsumedDeterministicIdDrops() for a future metric.
The deterministic trace_id bytes, the GenerateTraceId() consume logic, and
the unconditional reset() are all unchanged.

M2: tighten the Doxygen on SpanGuard::childSpan(std::string_view) to state
it parents to the current ambient context of this store (meaningful only
when a scope/ScopedSpanGuard/ScopedActivation is active) and to point
callers at childSpan(name, ctx) for explicit cross-store parenting.
Doc-only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 23:10:49 +01:00
Pratik Mankawde
d6ec7ce8af Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing
Brings coroutine-aware context storage + scoped rpc.command spans forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 20:05:35 +01:00
Pratik Mankawde
6a9eaedeac Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
Brings coroutine-aware OTel context storage forward: CoroAwareContextStorage,
its install in Telemetry, ScopedSpanGuard same-store assertion, and the
non-owning ScopedActivation helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:49:26 +01:00
Pratik Mankawde
e3c724423f feat(telemetry): add ScopedActivation for non-owning span activation
Add SpanGuard::activate() returning a ScopedActivation RAII helper that
activates an already-owned span (from a thread-free SpanGuard) as the
current context WITHOUT taking ownership. The activation pushes the span
onto the current LocalValue context store on construction and pops it on
destruction; it never ends the span (its owning SpanGuard does).

This lets a job-handoff span be made ambient for the duration of a
synchronous, non-yielding worker body so log lines there carry the
span's trace_id. Non-copyable and non-movable, mirroring ScopedSpanGuard.

owner is captured after the Scope push via declaration-order member
initialization (scope declared before owner), matching the A3
capture-after-materialization invariant. A #else no-op stub keeps the
API zero-overhead when telemetry is compiled out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:41:02 +01:00
Pratik Mankawde
7dbbf95c72 refactor(telemetry): ScopedSpanGuard asserts same context store, not thread
Coroutine-aware storage lets a scope resume on another worker within the same
coroutine store; store-identity is the correct pop-safety invariant. Same-store
equals same-thread for synchronous code, so no safety is lost.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 18:52:48 +01:00
Pratik Mankawde
f1b7104bb8 feat(telemetry): coroutine-aware OTel context storage
Backs the OTel active-context stack with xrpl::LocalValue so the ambient
context follows a JobQueue::Coro across yield/resume. Not yet installed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:56:16 +01:00
Pratik Mankawde
a0f8c87f56 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-22 16:20:29 +01:00
Pratik Mankawde
7fec5d75d5 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-22 16:20:28 +01:00
Pratik Mankawde
d865a266b5 fix(telemetry): clang-tidy naming + include-cleaner in DeterministicIdGenerator
- Rename file-local thread_local globals to the .clang-tidy convention
  (GlobalVariablePrefix "g" + CamelCase), keeping the Tls marker:
  tlsPendingTraceId -> gTlsPendingTraceId, tlsPendingConsumed -> gTlsPendingConsumed.
- Add direct includes for opentelemetry trace_id.h / span_id.h (header uses
  TraceId/SpanId in signatures) and sdk/trace/id_generator.h (.cpp references
  IdGenerator directly) to satisfy misc-include-cleaner.

Both verified clean with clang-tidy against a telemetry-enabled compile DB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:20:16 +01:00
Pratik Mankawde
6f868e36e5 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-22 14:44:15 +01:00
Pratik Mankawde
a25f18521e Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-22 14:44:15 +01:00
Pratik Mankawde
64cea3ded4 docs(telemetry): use rpc_span::op::process constant in @code examples
The OTel naming check (Rule F) scans @code doc-comment examples and fails
on string-literal span names; Rule H warns on undefined SpanNames
constants. Replace the literal "subtask" and the undefined
rpc_span::op::dispatch with the defined rpc_span::op::process constant so
the examples model correct API usage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 14:43:59 +01:00
Pratik Mankawde
3d2542dc6e Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-21 21:54:05 +01:00
Pratik Mankawde
c9fb16e67b Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
2026-07-21 21:53:54 +01:00
Pratik Mankawde
0e7ca13cf3 refactor(telemetry): split context capture into spanContext() (own span) + threadLocalContext() (current)
OTel distinguishes a span's own context from the thread's
current/ambient context; SpanGuard is unscoped so spanContext()
(own span, default) is what cross-thread childSpan parenting needs,
and threadLocalContext() (static) snapshots RuntimeContext::GetCurrent()
for propagation. Renames captureContext.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:48:40 +01:00
Pratik Mankawde
6d6d9eb862 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-21 20:39:42 +01:00
Pratik Mankawde
0c2457ed34 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 20:39:41 +01:00
Pratik Mankawde
f6f0e72224 docs(telemetry): DeterministicIdGenerator is active via hashSpan, not dormant
The generator's doc said it was 'dormant, caller lands on a later branch'.
Reworded to branch-agnostic language: hashSpan() is the primary caller that
forces the root branch to mint deterministic trace roots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 20:39:29 +01:00
Pratik Mankawde
c7b3271edb Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing
# Conflicts:
#	src/xrpld/rpc/detail/ServerHandler.cpp
2026-07-21 18:50:49 +01:00
Pratik Mankawde
d2d114595b Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 18:43:26 +01:00
Pratik Mankawde
4d95f455f4 feat(telemetry): add DeterministicIdGenerator + PendingTraceId for true-root deterministic trace_ids
Add a custom OTel IdGenerator that returns a thread-local pending trace_id on
the SDK no-parent (root) branch and a random one otherwise, plus a PendingTraceId
RAII guard that pins that id for the next forced-root span and asserts on
destruction that it was consumed. Wire the generator into
TracerProviderFactory::Create via its 4-arg overload.

This lets hash-derived spans become true trace roots so they line up into one
trace across nodes. It is installed but dormant on this branch: the caller
(hashSpan) arrives on a later branch (phase-3). GenerateSpanId is always random
and is_random_ is false so the W3C random-trace-id flag is not set on
deterministic ids.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 18:42:54 +01:00
Pratik Mankawde
508c91d36c refactor(telemetry): split SpanGuard into unscoped SpanGuard + scoped ScopedSpanGuard
Separate the two responsibilities the old SpanGuard fused: span ownership
(thread-free) and scope activation (thread-bound TLS push).

- SpanGuard now owns ONLY the span. Its Impl drops the optional<Scope>,
  the owner thread-id, the Detached tag and the scope-less ctor; ~Impl is
  just `if (span) span->End()`. The guard never binds a thread-local
  context stack, so it may be moved to and destroyed on any thread.
- ScopedSpanGuard is a new pimpl type that wraps a SpanGuard plus an
  optional<Scope>. Member order (guard first, scope second) pops the
  scope before the span ends. It is non-copyable and non-movable;
  factories return unnamed temporaries so guaranteed copy elision covers
  `auto s = ScopedSpanGuard::freshRoot(...)`.
- `operator SpanGuard() &&` replaces detached()/detachInPlace(): it pops
  the scope eagerly on the origin thread and yields a thread-free
  SpanGuard for handoff to a job or another thread. detached(),
  detachInPlace() (both overloads) and the Detached apparatus are deleted.
- Move-assignment is re-enabled on SpanGuard (no Scope to re-bind), in
  both the real class and the no-op stub.
- rootSpan() renamed to freshRoot() in both classes (behavior unchanged:
  still forces kIsRootSpanKey).
- hashSpan behavior is intentionally unchanged (only builds the now
  unscoped Impl); the true-root IdGenerator fix is a separate later task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 18:23:14 +01:00
Pratik Mankawde
2224f84573 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-21 11:44:10 +01:00
Pratik Mankawde
dcfaf39b0a Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 11:44:03 +01:00
Pratik Mankawde
f9b2d725a0 fix(telemetry): assert SpanGuard Scope is destroyed on its constructing thread
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:26:29 +01:00