getMeter() became a pure virtual on this branch (metric gap-fill), which
made the TestTelemetry mock abstract and broke the scope-leak test build.
Mirror NullTelemetry: serve an inert meter from a process-wide noop
provider. The scope tests exercise only tracing, so the meter is unused.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-review fixes for the SpanGuard scope-leak change:
- SpanGuard.h: restore the injectCurrentContextToProtobuf real-class
declaration, its #else no-op stub, and the protocol::TraceContext
forward-declaration dropped during the 3->4 union merge (compile break).
- Re-parent consensus.phase.open and consensus.establish under the round
span via a new RCLConsensus::Adaptor::roundSpanContext() accessor: the
round span is now detached, so ambient parenting no longer works; the
phase spans link explicitly to the captured round context. csf::Peer
gains a matching no-op accessor so the generic engine still compiles.
- Switch five childSpan(op::X, ctx) sites to the full consensus::span::X
constants: childSpan(name, ctx) takes the name verbatim, so the suffix-
only op:: constants emitted short, non-dotted span names.
- TestTelemetry mock: add getConsensusTraceStrategy() override (base pure
virtual introduced on this branch) so the mock is not abstract.
- validate(): rewrite the trace_context comment to drop attack-surface
framing and the PR-discussion reference.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
roundSpan_, establishSpan_, openSpan_ and the accept span are emplaced on
one job worker and reset()/moved to another, leaking their thread-local OTel
Scope. Each is now detached AFTER its context is captured. Spans that
previously nested under them via the ambient thread context are re-pointed to
the captured SpanContext explicitly:
- round children (proposal.send, ledger_close, mode_change) -> roundSpanContext_
- establish children (update_positions, check) -> establishSpanContext_
- accept.apply -> acceptSpanContext_
onForceAccept (synchronous) keeps the accept span's behaviour; only onAccept
(moved into the JtAccept job) needs the detach. Member move-assignment is
deleted, so re-assignment uses emplace().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The consensus.proposal.receive / consensus.validation.receive guards are
moved into checkPropose/checkValidation jobs, so detach their Scope on the
peer thread before the job hand-off. The standalone (no-propagated-context)
fallbacks in ConsensusReceiveTracing now use rootSpan() so they start a
fresh trace root instead of inheriting an ambient span; the hashSpan
propagated-context branches are unchanged (cross-node linking preserved).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
doRipplePathFind holds its pathfind.request span across context.coro->yield();
the coroutine resumes on a different JobQueue worker. rootSpan() gives it a
fresh trace root (no stale ambient parent), and detached() strips the
thread-local Scope so the guard neither lingers on the worker's context stack
across the yield nor pops the wrong stack on resume.
Adds SpanGuardScope.cpp: an in-memory-exporter test for rootSpan()/detached()
including the rootSpan().detached() combination used here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
These inbound peer-message entry points (kConsumer) used span(), which
inherits whatever span is active on the peer thread — including a leaked
tx.receive scope — so validations/proposals were wrongly nested under
unrelated transaction traces. rootSpan() starts a fresh trace root.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both spans are moved into job-queue lambdas and destroyed on a worker
thread. Detaching on the origin thread pops the thread-local OTel Scope
there, so later spans on the peer/RPC thread no longer inherit these as a
leaked ambient parent. Trace_id/parent are unchanged (both are hashSpan).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rootSpan has the same (cat, prefix, name) signature as span(), so its
prefix/name arguments must be *SpanNames.h constants under Rule F/H. Add
rootSpan to the CALLSITE regex and CONSTANT_ARG_POSITIONS ({1,2}), update
the enumerating docstrings/README, and add two regression tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The otel-naming Rule F check scans @code doc examples as call sites; raw
string literals there trip the rule. Reference *SpanNames.h constants /
placeholder identifiers in the examples so comments follow the naming rules.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
detached() strips the thread-local OTel Scope so a guard can be safely
moved to and destroyed on another thread; it pops the Scope on the origin
thread and moves the span into a scope-less guard. rootSpan() starts a span
as a fresh trace root (kIsRootSpanKey) so inbound entry points never inherit
an ambient span left active on the thread.
Impl now holds an optional<Scope> (nullopt for detached guards). Updated the
SpanGuard class docs and docs/build/telemetry.md with the cross-thread rules.
The unit test lands on phase2 where the telemetry test module exists.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sync the Node Health dashboard with panels and field settings refined in
Grafana, and document the ledger-convergence panels:
- Add descriptions for "Validated Ledger Seq — Current (Stat)" (per-node lag
behind the network tip) and "Validated Ledger Seq — Convergence (Max − Min)"
(seq spread across nodes), in the standard 7-field panel format.
- Peer Count: threshold colours (red < 5, yellow < 10, green >= 10) so a low
peer count reads red rather than using a value gradient.
- Minor field-config fixes on Last Close and NuDB Storage panels (axis label,
threshold cleanup) as adjusted in Grafana.
Add the Grafana duration unit "dthms" to the cspell dictionary. Panel content
stays at parity with the Grafana Cloud copy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regroup all 47 panels under correct subsystem rows and fix the broken row
structure. Previously the "NodeStore I/O" row had no gridPos and collided at
y=32 with an appended "Extended Metrics" catch-all row, so NodeStore I/O
rendered empty and its panels landed under the wrong header.
- Rows: Overview (top), Operating Mode, NodeStore I/O, Jobs, Caches, Server
Info, Complete Ledgers & DB, Ledger Economy. Drop the stray "Extended
Metrics" row; each panel now sits under its subsystem.
- Normalize panel heights to three buckets: 12 (standard), 16 (emphasis /
detail plots), 24 (Operating Mode state timeline). Widths stay half (12) or
full (24).
- Contiguous, non-overlapping gridPos; all 47 panels preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rework the legend identity bracket across all 14 dashboards so it reads
"<series> [instance, branch, work_item]" (comma-separated, Node Role dropped)
and collapses cleanly when perf-iac labels are absent -- "<series> [instance]"
with no empty "[, ]" gaps.
- Build one xrpl_ident label per query via label_join + a single label_replace
that trims leading/trailing empty segments and brackets the result. Done at
the outermost (post-aggregation) level so it survives each panel's by() clause.
- Add service_instance_id to the by() of 6 panels that grouped only by node_role,
so same-branch validator/peer series stay distinguishable after dropping the
node_role legend token.
- Fold the two legendFormat two-bracket panels (Transaction Results by Type,
Tx Apply Pipeline Latency by Type and Stage) into the same single-bracket form.
- Rename the annotation query label "Perf runs (perf-iac)" to
"Annotate perf-iac runs".
- Revert dashboard-level descriptions to plain text (the folder-list info tooltip
escapes HTML, so <br>/markdown render literally).
Verified: output byte-identical to the prior multi-call cleanup over a 2-day
live perf window; all 14 files valid JSON.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Group the node + perf-iac identity labels inside one pair of brackets in the
legend Display name so a series reads e.g. "Queue Depth [validator-0
test:pr:abc123 validator RIPD-7455]" on a perf node and "Queue Depth
[aws-dev-xrpl-1]" on a plain node. Absent perf-iac labels collapse to a single
space before the closing bracket; no empty "[]", no data impact. Covers the
eleven dashboards this branch owns.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Group the node + perf-iac identity labels inside one pair of brackets in the
legend Display name:
${__field.labels.series} [${__field.labels.service_instance_id} ${__field.labels.xrpl_branch} ${__field.labels.xrpl_node_role} ${__field.labels.xrpl_work_item}]
so a series reads e.g. "Queue Depth [validator-0 test:pr:abc123 validator
RIPD-7455]" on a perf node and "Queue Depth [aws-dev-xrpl-1]" on a plain node.
Absent perf-iac labels render empty and collapse to a single space before the
closing bracket (HTML/SVG whitespace collapse) — the only cosmetic artifact on
non-perf nodes; no empty "[]" and no data impact. Verified on a live local
Grafana render.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>