Commit Graph

14634 Commits

Author SHA1 Message Date
Pratik Mankawde
cc7585c0a8 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-21 21:57:35 +01:00
Pratik Mankawde
64a3c93188 test(telemetry): SpanGuardFactory uses spanContext() (captureContext renamed)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:57:22 +01:00
Pratik Mankawde
81805b514f Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing
# Conflicts:
#	src/libxrpl/telemetry/SpanGuard.cpp
2026-07-21 21:55:21 +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
b4db8d0797 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-21 21:07:24 +01:00
Pratik Mankawde
8a5c806346 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-21 21:07:24 +01:00
Pratik Mankawde
ec5cd97de6 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-21 21:07:24 +01:00
Pratik Mankawde
97e6586f71 fix(telemetry): captureContext() captures the guard's own span, not ambient
SpanGuard is unscoped (never pushed onto the thread-local context stack), so
RuntimeContext::GetCurrent() returned an unrelated ambient span. Build the
captured context from impl_->span so captureContext() is correct for every
caller regardless of scoping — childSpan(name, ctx) then parents explicitly to
this span across threads. Matches getTraceBytes()'s own-context behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:07:20 +01:00
Pratik Mankawde
bb49b02d7d Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-21 20:39:42 +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
deecae0f01 refactor(telemetry): tx spans thread-free (no detach); txq spans scoped for nesting
SpanGuard is now thread-free (holds no Scope), so the tx.receive (PeerImp)
and tx.process (NetworkOPs) handoff sites no longer need .detached() before
being stored and ended on a worker thread — just construct the guard. The
stale "Scope leak" comments are replaced accordingly.

Make the six txq.* spans ScopedSpanGuard so their sub-spans nest via the
ambient context: txq.apply_direct/batch_clear under txq.enqueue, and
txq.accept_tx under txq.accept. All six are verified synchronous, ended at
scope, and never moved/handed off, so scoping is safe. applyDirect's span
is pure RAII (no method calls), so it is declared const.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 20:15:31 +01:00
Pratik Mankawde
d63d5bd45e fix(telemetry): hashSpan standalone spans are true roots via DeterministicIdGenerator
The single-arg hashSpan fabricated a synthetic DefaultSpan parent (random
span_id) to carry the deterministic trace_id. That left every standalone
hash span with a phantom parent_span_id pointing at a span that is never
exported, so Tempo reported "root span not yet received".

Inject the deterministic trace_id through the DeterministicIdGenerator
instead: start the span on the SDK root branch (Context{kIsRootSpanKey,true})
with a PendingTraceId pinning hashData[0:16]. The SDK's no-valid-parent
branch calls GenerateTraceId(), which returns the pinned id, yielding a
TRUE root (empty parent_span_id) whose trace_id == hashData[0:16].

The deterministic bytes are unchanged (still raw hashData[0:16]). The
cross-node overload (real remote parent, remote=true) is untouched and must
keep producing a child of the sender's span. Drop the now-unused
<xrpl/basics/random.h> include; add <array> and DeterministicIdGenerator.h.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 20:10:10 +01:00
Pratik Mankawde
14d3d5baa3 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
#	src/libxrpl/telemetry/SpanGuard.cpp
2026-07-21 20:01:54 +01:00
Pratik Mankawde
e4d02904ad test(telemetry): rewrite SpanGuard/ScopedSpanGuard + deterministic-root tests; fix RipplePathFind
Rewrite SpanGuardScope.cpp for the unscoped SpanGuard / scoped
ScopedSpanGuard split and the DeterministicIdGenerator: install the
generator in the TestTelemetry mock (4-arg TracerProviderFactory), drop the
obsolete detached()/detachInPlace() tests, and add freshRoot, scoped-ambient,
scope-handoff, ends-once, deterministic-root, and cross-thread death tests
with exact assertions.

Fix the last detached() caller: RipplePathFind now uses SpanGuard::freshRoot
(SpanGuard is thread-free, so it is held across the coroutine yield and ended
on resume with no scope to strip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 19:38:10 +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
4f68c97627 fix(telemetry): root RPC entry spans so each request is its own trace
Entry points (http_request/ws_message/ws_upgrade) were inheriting a leaked
ambient span on reused coro workers; now scoped fresh roots via
ScopedSpanGuard::freshRoot. rpc.process stays a scoped child.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 18:47:55 +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
b7037d3872 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing
# Conflicts:
#	src/libxrpl/telemetry/SpanGuard.cpp
2026-07-21 11:45:06 +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
8d8bc8b928 fix(telemetry): widen death-test regex for split assert literal, skip under NDEBUG
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:41:29 +01:00
Pratik Mankawde
f59682fb75 test(telemetry): convert cross-thread scope-leak negative control to a death test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:30:47 +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
Pratik Mankawde
9bcc3576f6 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing
# Conflicts:
#	src/libxrpl/telemetry/SpanGuard.cpp
2026-07-20 21:41:45 +01:00
Pratik Mankawde
2a0a14c127 test(telemetry): add SpanGuardScope coverage for detachInPlace() helpers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 21:37:41 +01:00
Pratik Mankawde
9e61615a08 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-20 21:33:02 +01:00
Pratik Mankawde
1ed1cb9573 Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-07-20 21:32:54 +01:00
Pratik Mankawde
b9c049d7c6 refactor(telemetry): add detachInPlace() helpers to remove hand-rolled SpanGuard re-detach idiom
Add two free functions in xrpl::telemetry that wrap the required
"detach a live SpanGuard held in a container" idiom so call sites write
one line instead of the make_shared/emplace rebuild by hand:

  void                detachInPlace(std::optional<SpanGuard>&);
  std::shared_ptr<SpanGuard> detachInPlace(std::shared_ptr<SpanGuard>);

Both are no-ops on an empty/null/inactive guard. The #else branch adds
matching inline no-op stubs so callers compile with telemetry disabled.
Pure API addition, no behavior change; consumer call sites are rewritten
on phase4 in a follow-up.

The unit tests for these helpers land on phase2, where the telemetry
test module (in-memory-exporter harness + SpanGuardScope.cpp) exists —
mirroring how the detached()/rootSpan() tests were placed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 21:30:05 +01:00
Pratik Mankawde
be268576de Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-20 17:55:16 +01:00
Pratik Mankawde
58d93aaf46 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-20 17:54:50 +01:00
Pratik Mankawde
8d83751011 ci(telemetry): recognize external-infra identity labels in otel-naming Rule D
xrpl_branch and xrpl_node_role are stamped by the perf-iac harness --
infrastructure outside this repo's OTel code -- so they have no L1
(*SpanNames.h), L2 (collector config), or L6 (MetricsRegistry.cpp) source
Rule D can derive them from. Unlike the generic builtins set (Prometheus/
Grafana mechanics every OTel setup has), these are repo-specific and
narrow, so they get their own EXTERNAL_INFRA_LABELS constant: a visible,
documented, deliberately narrow exception to the 'no hardcoded allowlist'
design principle, not a silent workaround. Add test coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:54:30 +01:00
Pratik Mankawde
c5f419121f Revert "ci(telemetry): recognize perf-iac identity labels in otel-naming Rule D"
This reverts commit 48747bab4c.
2026-07-20 17:50:19 +01:00
Pratik Mankawde
48747bab4c ci(telemetry): recognize perf-iac identity labels in otel-naming Rule D
xrpl_branch and xrpl_node_role are collector/infra-injected resource
labels used by perf-iac dashboards to identify the build under test and
its role in the perf cluster. They have no L1 span-attribute source or
L6 native-metric-label source (like the existing job/instance/job_type
builtins), so Rule D flagged them across every perf-iac dashboard.
Register them in the builtins set, matching the existing pattern for
infra-level dashboard labels. Add test coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:49:44 +01:00
Pratik Mankawde
5dc7eb25b8 chore(telemetry): normalize remaining triple-slash Doxygen comments
Convert leading /// blocks in these 9 phase3-originating files to house
style /** */ (using the updated fix_doxy.py that now handles /// ->
block conversion). Comment-only: code is byte-identical.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:41:37 +01:00
Pratik Mankawde
6e99da094c Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-20 17:19:07 +01:00
Pratik Mankawde
abd05cb939 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing
# Conflicts:
#	.github/scripts/levelization/results/loops.txt
2026-07-20 17:18:53 +01:00
Pratik Mankawde
daea7fa34f Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration
# Conflicts:
#	include/xrpl/telemetry/Telemetry.h
2026-07-20 17:17:20 +01:00
Pratik Mankawde
8b9cb43824 chore(telemetry): normalize Doxygen comment style + refresh levelization
Bring the 5 telemetry files introduced on this branch to the enforced
house Doxygen style (/** alone, ' * ' continuation prefix, no single-line
blocks) so the check-doxygen-style hook passes under CI's --all-files run.
Comment-only: code is byte-identical after comment stripping (verified).

Also regenerate levelization results: the committed ordering.txt carried
stale 'xrpl.telemetry > xrpld.consensus/rpc' edges that the current include
graph no longer produces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:09:43 +01:00
Pratik Mankawde
04cb7ee87f Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-20 16:33:55 +01:00
Pratik Mankawde
dbf7652e19 fix(telemetry): link in-memory exporter archive by path, not component
The opentelemetry-cpp::exporter_in_memory component declares no libs (the
Conan recipe ships libopentelemetry_exporter_in_memory.a but does not
associate it with the component), so linking that target put nothing on
the link line and xrpl_tests still failed with an undefined reference to
InMemorySpanExporterFactory::Create. Locate the archive with find_library
(HINTS the OTel package lib dir) and link it by path, ahead of the umbrella
so its undefined SDK references resolve. Verified: the archive now appears
on the generated link line and xrpl_tests links + the SpanGuardScope tests
pass. Supersedes the no-op component-target link in the prior commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:30:28 +01:00
Pratik Mankawde
02ddf6a31f Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-07-20 16:04:31 +01:00
Pratik Mankawde
63e2febb70 fix(telemetry): link in-memory exporter component into xrpl_tests
The umbrella opentelemetry-cpp target's link set omits the in-memory
exporter library, so xrpl_tests failed to link with an undefined
reference to InMemorySpanExporterFactory::Create. The SpanGuardScope
tests use an InMemorySpanExporter to read exported spans. Link the
opentelemetry-cpp::exporter_in_memory component explicitly. Test-only:
production code never uses this exporter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:04:17 +01:00
Pratik Mankawde
4e9b2f23d6 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
#	src/libxrpl/telemetry/SpanGuard.cpp
#	src/libxrpl/tx/applySteps.cpp
2026-07-20 12:03:57 +01:00
Pratik Mankawde
d7a014ae95 Merge branch 'pratik/otel-phase1c-rpc-integration' into pratik/otel-phase2-rpc-tracing 2026-07-20 11:50:55 +01:00