Commit Graph

42 Commits

Author SHA1 Message Date
Pratik Mankawde
9e6dd645c7 style: Rename gtest test cases to snake_case
develop's new fix-gtest-names pre-commit hook requires a snake_case test-case
name. CI runs it with --all-files, so the existing CamelCase names fail the
check on every branch that carries these files.
2026-09-23 19:05:21 +01:00
Pratik Mankawde
64d080178b Merge branch 'pratik/otel-phase10-workload-validation' into pratik/otel-sync-diagnostics 2026-09-23 19:05:20 +01:00
Pratik Mankawde
57e0d13885 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-09-23 19:04:56 +01:00
Pratik Mankawde
0212c8dc47 style: Rename gtest test cases to snake_case
develop's new fix-gtest-names pre-commit hook requires a snake_case test-case
name. CI runs it with --all-files, so the existing CamelCase names fail the
check on every branch that carries these files.
2026-09-23 19:04:51 +01:00
Pratik Mankawde
3d769ad9c3 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing 2026-09-23 19:04:50 +01:00
Pratik Mankawde
a0c3066140 style: Rename gtest test cases to snake_case
develop's new fix-gtest-names pre-commit hook requires a snake_case test-case
name. CI runs it with --all-files, so the existing CamelCase names fail the
check on every branch that carries this file.
2026-09-23 19:04:28 +01:00
Pratik Mankawde
f23b66ca4d Merge branch 'pratik/otel-phase10-workload-validation' into pratik/otel-sync-diagnostics
expected_spans.json: composed both sides. Kept this branch's 16 extra spans,
relationships and the per_ledger join group; took phase-10's allowed_parents
gate for the shared entries and derived allowed_parents for the 16 from their
factories. ledger.store, ledger.validate and ledger.acquire are hashSpan roots
on this branch, so they list ROOT alone rather than consensus.accept.apply.
2026-09-23 15:18:25 +01:00
Pratik Mankawde
360cde04ef Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-09-23 13:57:48 +01:00
Pratik Mankawde
59bae37688 fix(telemetry): nest the accept work under consensus.accept.apply
accept.apply was a plain SpanGuard, so it never became the ambient span of
doAccept. The spans the function goes on to create inherited the activated
accept span instead and came out as accept.apply's siblings, while running
inside its own time window. Every guard was scoped before the SpanGuard split,
so this restores the hierarchy that design had.

Scoped now, so the hierarchy follows the call flow. Drops the parent-context
fallback arm with it: the accept context is captured only while the accept span
is live, and that span is a child of the round context, so an invalid accept
context implies an invalid round context and both arms returned an empty guard.
2026-09-23 13:49:42 +01:00
Pratik Mankawde
7d21baf558 feat(telemetry): add event-with-attributes overload to ScopedSpanGuard
Only SpanGuard carried addEvent(name, attrs), so a call site holding a scoped
guard could not record an event attribute. Forwarding overload, with the no-op
twin in the telemetry-disabled stub, so a span can be converted between scoped
and unscoped without dropping the attributes on its events.
2026-09-23 13:49:03 +01:00
Pratik Mankawde
881faf5620 Merge branch 'pratik/otel-phase10-workload-validation' into pratik/otel-sync-diagnostics
The runbook's nodestore_state table kept AppMetricGauges.cpp as the source
column, which is where those gauges are actually registered on every branch
in the chain; the incoming side named MetricsRegistry.cpp, which registers
none of them. Took the incoming side's clearer wording for the sweep row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 21:19:08 +01:00
Pratik Mankawde
e48b0286c2 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
StatsDCollector test kept both sides: this branch's onCollectionReady() call,
which enables polling and only exists from here on, followed by the upstream
branch's control assertion that reads the resulting datagram.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 21:17:48 +01:00
Pratik Mankawde
aea4f56505 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing
SpanGuardScope.cpp kept both includes: each side added one and both symbols
are used in the merged test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 21:15:05 +01:00
Pratik Mankawde
87560c5157 test(telemetry): Restore the thread's LocalValue store from a scope guard
The store swap was undone by trailing statements, which a fatal assertion skips.
The thread store is a function-local static, so it outlives every test rather
than being reset between them: a skipped restore left it owning a stack object
from a dead frame, and the crash then landed in whichever test ran next.

The onCoro flag did not protect it either, because the cleanup function reads
that flag out of the freed object to decide not to delete it.

The guard is declared after both stack stores so it is destroyed before them.
2026-09-22 20:31:15 +01:00
Pratik Mankawde
545ec26eed merge: bring the noopMeter() clang-tidy fixes forward from phase10-workload-validation 2026-09-14 23:48:42 +01:00
Pratik Mankawde
7a4806c00a fix(telemetry): satisfy clang-tidy on the noopMeter() helper
Static constants take the k prefix (readability-identifier-naming), and
SpanGuardScope.cpp no longer names anything from <opentelemetry/metrics/noop.h>
since it calls noopMeter(). Both fail CI under warnings-as-errors.

The helper's docstring also claimed NoopMeterProvider hides the base
two-argument GetMeter; it declares that overload itself, so the only
detail worth sharing is the version.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 23:47:55 +01:00
Pratik Mankawde
1880c9a498 merge: bring phase10-workload-validation forward into sync-diagnostics
Resolutions:
- MetricsRegistry.cpp: keep both <exception> and <limits>; drop
  incrementStateChanges(), which this branch removed on purpose (the
  labelled state_changes_total call site in NetworkOPsImp::setMode
  replaces it, and a compile-time test guards that).
- tests/MetricsRegistry.cpp: constructor-built pipeline wording from
  phase-10, this branch's test list and gauge paragraphs kept; the two
  lifecycle tests now call startAsyncGauges() and pass kTestOptions.
- tests/MetricMacros.cpp: comments name the recording() gate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 23:45:32 +01:00
Pratik Mankawde
bec9e1c8a9 fix(telemetry): resolve the node identity before the Application is built
resolveNodePublicKey() returned std::nullopt in three real cases: a first boot
with no wallet database, a standalone run (its wallet is a private temporary
database), and --newnodeid. Telemetry's resources are built during
ApplicationImp's member-init list and are immutable, so on those runs the node
reported an empty service.instance.id and no xrpl.node.id for the whole run,
while setup() minted a key moments later and patched only the tracer.

Replace it with resolveNodeIdentity(), which always returns a keypair: derived
from a configured seed, else read from an existing wallet database, else
minted. Main.cpp passes that pair to makeApplication(), ApplicationImp stores
it in nodeIdentity_ -- now declared before telemetry_ and no longer an optional,
because it is always set -- and builds the telemetry resource from it.

setup() calls getNodeIdentity(), which now persists rather than mints: it
stores the resolved pair when the wallet holds no identity, adopts the stored
one when it does, and clears first for --newnodeid. The write stays in setup()
because that is where the database exists; a standalone run has no persistent
wallet to write to, which is why the pair has to be decided before
construction rather than read back afterwards. Wallet gains storeNodeIdentity()
for that write, and getNodeIdentity(session) now uses it instead of repeating
the insert.

The three-argument makeApplication() mints a keypair, so jtx::Env and any other
test Application behave as a standalone run always did.

Also fold the three hand-rolled "meter from a NoopMeterProvider" copies into
telemetry::noopMeter(): the base-pointer call and the kMeterVersion argument are
both easy to get wrong alone, and the meter identity has to match the one the
histogram views select on.

The new gtest covers the wallet half: store-then-read, store not replacing an
existing identity, clear-then-store, and that the mint path persists. It adds
the tests.libxrpl > xrpl.rdb levelization edge, regenerated here.
2026-09-14 20:34:31 +01:00
Pratik Mankawde
0fab743c6d merge: bring the review fixes forward from otel-phase10-workload-validation 2026-09-08 15:55:05 +01:00
Pratik Mankawde
7d679ce596 merge: bring the review fixes forward from otel-phase6-statsd 2026-09-08 15:45:22 +01:00
Pratik Mankawde
fb827dc0f1 fix(telemetry): make the consensus trace strategy an enum
consensus_trace_strategy was read as a std::string and compared against the
literal "attribute" in startRoundTracing(), while the runbook documented
"deterministic" and "random". The documented value "random" therefore fell
through to the default and did nothing.

Parse the setting once into ConsensusTraceStrategy, so the consensus code
branches on a type. The accepted spellings are now "deterministic" and
"random"; anything else fails at startup instead of silently defaulting.
The behaviour behind the old "attribute" name is unchanged and is now
reached by "random".

Document consensus_trace_strategy in xrpld-example.cfg, stating that
"random" is experimental and not used: it gives each node its own trace id,
so one round arrives as one trace per node.

Also state on the tx.included event that it covers the agreed consensus set
before the ledger is built, so it is a superset of the accepted ledger.
2026-09-08 14:39:05 +01:00
Pratik Mankawde
3c56697930 merge: bring the review fixes forward from otel-phase10-workload-validation 2026-09-07 15:47:20 +01:00
Pratik Mankawde
4ad84cae14 merge: bring the review fixes forward from otel-phase6-statsd 2026-09-07 15:23:22 +01:00
Pratik Mankawde
b89a83f8f8 fix(telemetry): stamp the round span's mode when the engine applies it
startRoundTracing runs as an argument to Consensus::startRound, so it creates
consensus.round before startRoundInternal applies the new mode. Reading mode_
there recorded the previous round's value, and a validator switching from
observing to proposing got a round span labelled observing that nothing corrected.

The attribute is now written in onModeChange, from the mode being applied. All
three MonitoredMode::set paths funnel through there, so round start, a wrong-ledger
switch and a bow-out all correct the parent span with one statement. Every path
reaches it under RCLConsensus::mutex_ on the thread that created the span.

The stale write is removed rather than kept alongside: neither Consensus::startRound
nor startRoundInternal has an early return before mode_.set, so every round span is
stamped. If a future path ever skipped it the attribute would be absent, which reads
as a gap, instead of confidently wrong. onClose also sets consensus_mode, from the
engine's own parameter, and is correct as it stands.

Also tests addEvent's attribute overload on a live span, reading the exported event
name and each value back off the in-memory exporter. It was previously only ever
called on a null guard, so a dropped attribute exported nothing and failed nothing.
2026-09-07 14:39:50 +01:00
Pratik Mankawde
a214db3a90 docs(telemetry): remove pre-squash and plan-internal references from sync diagnostics
Comments across the sync-diagnostic work described earlier revisions of the same
change, or cited identifiers a reader of the merged tree cannot resolve.

Prior-state comparisons rewritten in the present tense:

- MetricsRegistry.cpp carried two adjacent paragraphs prescribing opposite
  behaviour for a disabled quorum, one publishing int64 max and one omitting
  the series. The code omits it; the superseded paragraph is gone and the
  surviving reason SIZE_MAX must not be cast is kept.
- MallocTrim, LedgerMaster, LedgerReplayTask, TransactionAcquire, Application:
  say what the signal is the only record of, rather than what was 'previously
  trace-only', 'not logged at all here' or 'used to sit inside if (debug())'.
- LedgerMaster.h and SpanGuardScope: without an explicit join each ledger's
  spans WOULD be separate traces -- not that they were 'before this'.
- Handshake: the message is forwarded byte for byte, not 'byte-identical to the
  previous behaviour', and the helper throws rather than 'throws as before'.
- MetricNames: quorum_disabled is a separate boolean rather than a sentinel,
  stated without what the state 'used to be encoded by'.
- LedgerMaster.cpp no longer claims to mirror the unl_quorum gauge; it does not.
  That gauge omits the series while this stores int64 max.
- 'Split out of' / 'Split from' become 'Kept separate from' in five places.

Plan-internal identifiers removed:

- All 24 WP-Ax / WP-Bx work-package labels across the telemetry tests, the
  collector configs, tempo.yaml and the expected_* inventories. They are defined
  in no file in the repo, so they resolve nowhere once merged.
- The two references to OpenTelemetryPlan/, which does not reach develop, now
  point at docs/telemetry-glossary.md 'Fresh-node sync diagnostics'.

Comments and JSON note strings only, no behaviour change.
2026-09-02 19:48:18 +01:00
Pratik Mankawde
41b818b55b feat(telemetry): join a ledger's spans into one trace, add round histogram (WP-B3)
A slow fresh-sync ledger produced spans scattered across threads with no
way to relate them. They now share a trace id derived from the ledger's own
hash, the one value every participating site already holds, so nothing new
is plumbed across threads. This is the pattern the transaction pipeline
already uses for its tx id.

Joined: ledger.validate, ledger.store, and a new
consensus.validation.accept recorded when a trusted validation arrives. In
Tempo, searching one ledger hash returns them together, so an operator can
tell whether the ledger was slow to arrive, slow to be accepted, or slow to
be stored. They are siblings rather than a chain because the accept gate is
entered from three different threads, so no fixed parent order exists.

consensus.validation.accept also records why an arriving validation did or
did not advance the gate, which makes "validations arrive but are all
rejected" visible for the first time.

consensus_round_duration_ms turns the existing round-time span attribute
into a histogram, so a fleet trend needs a metric query rather than raw
trace inspection. An explicit bucket view is required, not optional: the
SDK default tops out at ten seconds while consensus abandons a round at two
minutes, so slow rounds would all fall in one bucket and every quantile
would read exactly ten seconds. Cost is one record per round.

Record layer: the histogram is native and needs no collector change. The
two new bounded attributes are added as span-metric dimensions to both
collector configs. The ledger hash stays out of them, since a per-ledger
dimension mints a series per ledger; it is indexed in Tempo as the join key.

The ledger.acquire span is not joined yet, because that file was being
changed concurrently. It is registered as an optional member of the join
group so nothing fails, and switching it is a one-line follow-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 20:19:25 +01:00
Pratik Mankawde
3d495f9622 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
Brings coroutine-aware context storage + tx/consensus worker-body activation.
Resolved: Telemetry.cpp keeps both meterProvider_ (phase-7) and contextStorage_
(coro-aware); doc-09 keeps phase-7 structure and applies the pathfind.request →
rpc.command.<name> correction to phase-7's own PathFind section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:46:54 +01:00
Pratik Mankawde
7de0229450 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing
Brings coroutine-aware context storage + tx worker-body activation forward.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:15:38 +01:00
Pratik Mankawde
8192da5243 test(telemetry): coro-store-swap + activate tests; revert pathfind.request to scoped
Add two GTests on the SpanGuardScopeTest fixture and install a
CoroAwareContextStorage so the tests exercise coro-aware ambient context:

- scopedGuard_survives_localvalue_store_swap: a ScopedSpanGuard's scope
  is hidden when its LocalValue store is swapped out and visible again
  when swapped back in, and pops cleanly under the owning store.
- activate_sets_ambient_without_owning: SpanGuard::activate() makes the
  span ambient for the activation's lifetime without ending it; the
  owning guard ends it exactly once.

Fix the cross-store death test to match the store-identity assertion
message ("constructing context store", not "constructing thread") after
the A3 refactor; the fixture's storage install is what lets the assert
fire at all.

Revert RipplePathFind's pathfind.request from freshRoot SpanGuard back to
ScopedSpanGuard: the coro-aware storage moves the scope with the
coroutine across yield, so it nests under rpc.command and stays
trace-correlated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 21:35:27 +01:00
Pratik Mankawde
eec3062c9c Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-07-22 10:58:50 +01:00
Pratik Mankawde
fbb79d73fb Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing
# Conflicts:
#	src/libxrpl/telemetry/SpanGuard.cpp
2026-07-21 20:43:10 +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
d4eac3cbbc Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-07-21 11:45:34 +01:00
Pratik Mankawde
75cbf35e0d Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing 2026-07-21 11:45:15 +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
e0a472a411 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-07-20 21:51:20 +01:00
Pratik Mankawde
95056a49da Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing 2026-07-20 21:41:54 +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
494984112a fix(telemetry): add getMeter override to test mock (originating branch)
getMeter() became a pure virtual on THIS branch (native-metrics), which
makes the TestTelemetry mock abstract and would break a telemetry=ON
build of phase7/phase8 in isolation. The override was previously only on
phase9 (where it was mis-attributed); relocate it to phase7 where the
pure virtual is introduced, so every branch from here forward builds.
Mirrors NullTelemetry: an inert meter from a process-wide noop provider.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:51:02 +01:00
Pratik Mankawde
58d4e48679 fix(telemetry): restore injectCurrentContextToProtobuf decl; re-parent open/establish spans under round; correct childSpan names
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>
2026-07-20 15:25:15 +01:00
Pratik Mankawde
c1e3348c50 fix(telemetry): root and detach the RipplePathFind span held across coro yield
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>
2026-07-17 23:06:43 +01:00