Commit Graph

428 Commits

Author SHA1 Message Date
Pratik Mankawde
c7cacfb0e4 fix(telemetry): plot peer supply window as margins, not absolute sequences
The Peer Ledger Supply Window panel drew supply_min_seq, supply_max_seq and
nothing else on one linear axis. Measured on a mainnet node, those sit around
105,890,000 and roughly 300,000 apart, so the 588-ledger tip movement that
shows whether sync is progressing was 0.0006% of the axis and read as a flat
line. unit "none" also printed the sequences unabbreviated and clipped the
legend.

The panel's own "Watch for" text asked the reader to compare supply_min_seq
against this node's validated sequence, but that line was not on the panel at
all, so the comparison meant switching dashboards.

Plot the two distances instead, which is what the panel was always asking
about:

  History Headroom = validated_ledger_seq - supply_min_seq
  Tip Gap          = supply_max_seq - validated_ledger_seq

Zero is now the boundary in both directions: negative headroom is exactly the
"every peer pruned what I still need" case the description warns about, and it
becomes a zero crossing rather than a line-order comparison. Tip Gap gets the
right-hand axis because the two ranges differ by orders of magnitude
(measured: 299999..300001 against -1..1).

Both operands are gated `> 0`. Ungated, differencing the documented
"unknown window" sentinel of 0 yields the whole sequence space: measured
-105854935 for headroom and 105890295 for tip gap during the first ticks,
which destroys the axis for the rest of the window. Gated, the panel stays
blank until the node has a validated ledger and a peer has advertised a
range, which is the honest reading for that state.

Both queries verified against a live mainnet node through the full template
substitution: refId A = 300001 legend "History Headroom [xrpld-mainnet]",
refId B = -1 legend "Tip Gap [xrpld-mainnet]".

Runbook branch-C table, step 11 walkthrough and the 09 reference row follow
the rename and the new reading.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 21:23:46 +01:00
Pratik Mankawde
8633df7a3e feat(telemetry): expose the sweep-trim and rotation costs (WP-B5)
Two suspects from the 3.3.0 slowdown investigation had no signal. Both were
already computing the numbers and throwing them away, so this exposes them
rather than adding measurement.

Per-sweep heap trim. The trim runs after every cache sweep, and its cost
scales with resident heap, so it is the leading explanation for a node with
a populated database syncing slower than a fresh one. The report already
carried duration, fault deltas and reclaimed pages, but the whole
measurement sat behind a debug-journal check, so an ordinary node measured
nothing, and the call site discarded the result. The measurement now always
runs and only the log line stays gated. Records trim duration, minor faults
and reclaimed kilobytes. Measured cost of the always-on path is about six
microseconds per sweep against a trim costing milliseconds, at a cadence of
ten to a hundred and twenty seconds.

Honest limit, stated in the runbook: the fault delta spans only the trim
call, so it shows the trim itself faulting but not the faults that follow as
caches refill. The duration is the signal to correlate against sweep-job
queueing.

Rotation writes. Rotation copies archive-served reads forward and re-stores
nodes missing from both backends, both of which compete with sync I/O and
only happen on a populated online_delete database. The copy-forward count
existed but was reset by the rotation's own log line, so a metric reading it
would drop to zero on every swap; a never-reset total sits beside it now.
The re-store count was not measured at all. Rotation duration is
deliberately not recorded: the health throttle sleeps at eight points inside
the sequence and dominates exactly when the node is unhealthy, so the number
would conflate work with waiting.

Nothing added for the other two suspects. Get-object serving is already
covered by the handler label, the lookup histogram and the deferred and
saturation gauges; peer churn by the disconnect-reason counter.

Also replaces nine per-file cspell ignores with one ignoreRegExpList entry
for the telemetry macro names, and picks up the levelization baseline for the
consensus span-name test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:39:09 +01:00
Pratik Mankawde
371f10934e fix(telemetry): correct five signals that would have misled an operator
Found by reviewing what each metric actually measures, with attention to the
derived and bucketed ones. All five could report healthy while the node was
not, or the reverse.

- The nodestore latency panel took rate() of a mean. The gauge already
  divides duration by count in code, so rating it produced a figure with no
  unit, and Prometheus discards a gauge's decreases, so a heavy back-fill
  read as roughly zero microseconds per operation. The cumulative duration
  totals are now exported alongside the means, and the panel divides the
  rate of the total by the rate of the count, which is the latency over the
  panel's own window rather than a since-boot average that flattens with
  uptime.
- The DNS-resolve and outbound-dial histograms had no explicit buckets, so
  they inherited a ladder that stops at ten seconds while the dial timer is
  fifteen. Every timed-out dial fell in the overflow bucket and p95 read
  exactly ten seconds however bad it got. Both now have a ladder reaching
  thirty seconds with fifteen on its own boundary, so a timeout is
  distinguishable from merely slow.
- The missing-node counts only cleared when a tree completed, so a
  timed-out or failed acquire left its last count latched. Since the gauge
  reports the maximum across everything still in the collection, and
  eviction waits on a grace period plus the sweep interval, a finished node
  reported as stuck for minutes. That inverts the one signal that separates
  stuck from slow. Cleared unconditionally on the terminal path instead.
- A disabled quorum published a sentinel so large that, on a timeseries
  axis shared with the trusted-key count, it flattened the key line to the
  baseline and hid the outage it was meant to mark. The series is now
  omitted and a quorum_disabled flag carries the state.
- Two panel descriptions claimed a one-second export cycle. The reader is
  configured for ten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 14:27:28 +01:00
Pratik Mankawde
c5655cd42d Merge branch 'pratik/otel-phase10-workload-validation' into pratik/otel-sync-diagnostics
Phase-10 independently instrumented the peer object-fetch path while this
branch instrumented fresh-node sync, so the two overlapped in three places.
Resolved by keeping each side's stronger implementation rather than shipping
both.

Per-job-type waiting/running/deferred existed twice. Phase-10's version
survives: it publishes per-type gauges from JobQueue::collect(), which
snapshots under the queue lock and publishes after releasing it, a
deliberate lock-order fix against the collector's own lock. This branch's
jobq_backlog gauge and the JobQueue::getJobTypeCounts() accessor that fed it
are removed, along with their panels, assertions and reference rows.
jobq_saturation stays: it reports the whole worker pool, which phase-10 has
no equivalent for.

The histogram view helper also existed twice with identical bodies under two
names; one survives, and the microsecond ladder is now the named array
rather than boundaries repeated inline. The job_type label was declared
twice, once as a file-local constant invisible to the naming check; both it
and handler now come from the constants header.

Two things phase-10 adds are complementary, not duplicates, and are kept as
they are: the handler label, which separates the two request kinds that both
report as the same job type, and getobject_rejected_total, which counts
malformed requests where this branch's serve_refused_total counts requests
this node declined to serve.

Also fixes two naming-check failures that pre-date this merge on phase-10.
The check derived label keys only from namespaced constants, so it could not
see the per-subsystem headers' flat k-prefixed style and rejected dashboards
querying labels the code really emits. It now reads both styles, with the
enforcement rules unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 13:42:03 +01:00
Pratik Mankawde
22fd5e8601 feat(telemetry): make the sync board readable, and time real node writes (WP-B4)
The board and runbook had grown by append across eight work packages, so
they read in the order the work was done rather than the order a node
progresses. This is the coherence pass; it adds no new instrumentation.

- Dashboard: 52 panels regrouped from two rows into nine that follow the
  fresh-start sequence — bootstrap, peer supply, sync state, acquire and
  SHAMap fetch, job queue, quorum and publish, terminal blockers, then
  back-fill and spans collapsed since they answer conditional questions.
  Layout only: no title, query or description changed.
- Runbook: the flat step list becomes a decision tree branching on the
  observed symptom, with the amendment-block check first because it is
  terminal. Each branch names the panels, what healthy and unhealthy look
  like, and what to conclude. The existing steps are kept as the detail
  bodies.
- Reference table: every signal name re-checked against the code and every
  named panel against the board; four stale panel references fixed.
- Validation: every signal is now either asserted or covered by a note
  explaining why a five-node local cluster cannot produce it.

Also fixes the write-latency signal, which was inert on a real node: the
store duration was only recorded on the database-import path, while the two
production store implementations did not time themselves, so an ordinary
node reported a write count with no latency. Both now time the backend
write, which is the disk work this signal exists to expose. Without it the
"existing database syncs slower than a fresh one" diagnosis had no primary
signal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 11:12:26 +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
92729bacce feat(telemetry): render, assert and document the quorum signals (WP-A5)
The quorum and publish gauges were emitted but never surfaced: no panel, no
harness assertion, no reference entry. Completes those layers.

- Four panels: trusted validations against the quorum target on one axis so
  a tally climbing toward quorum is visually distinct from one flat below
  it; publish lag; pre-accept shortfall rate; and time to first validated
  ledger.
- Both signals are asserted by the workload validator. The shortfall
  counter does fire on a healthy cluster, because this node validates and
  then immediately re-enters the accept gate before its peers' validations
  arrive, so the first evaluation of every round tallies short. The panel
  and note say so, and give the fault signature instead: the shortfall rate
  outpacing the ledger-close rate while the tally stays flat and nothing
  ever reaches first-validated.
- The quorum target is deliberately drawn as its own line rather than as a
  headroom stat, so the disabled-quorum sentinel reads as an unreachable
  target instead of an unreadable negative number.

Also removes three reference rows that were appended twice when two agents
each documented the same back-fill signals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 17:22:28 +01:00
Pratik Mankawde
827525c86b feat(telemetry): wire A5-A7 and B1 signals through the pipeline
Registers the new gauges, renders them, asserts them and documents them, so
each signal reaches an operator rather than stopping at the emit site:

- MetricsRegistry: gauge registration for ledger_quorum_publish,
  nodestore_latency, peer_ledger_supply, peerfinder_slot_census and
  amendment_block, each guarded by the detached-callbacks check and
  tolerant of services that are not ready yet.
- Ledger Sync Health dashboard: panels for the new signals, filtered by
  the node template variable like every other board.
- Workload validation: the new series are asserted, so a signal that
  regresses to absent fails CI. Signals the local cluster structurally
  cannot produce, such as a replay fallback or an amendment block, are
  noted rather than asserted, which would fail red on a healthy run.
- Reference, runbook and glossary entries, including the diagnosis order
  for a node that has peers and validators but never validates.
- Regenerated levelization baseline: three new one-way edges from the
  telemetry and test modules, no new cycles.

Also drops an unused cstddef include from the macro tests, which the
include checker rejects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 16:15:26 +01:00
Pratik Mankawde
4115617eb9 feat(telemetry): add job-queue occupancy and saturation gauges (WP-A4)
Sync-critical job types run at very low concurrency limits (ledgerRequest
and ledgerData allow 3 each), so a node can stall simply because those
jobs are held back behind other work. Nothing exposed that until now:
the existing job metrics are rates and quantiles of jobs that already
moved, or a single queue-wide depth.

- jobq_backlog{metric,job_type}: instantaneous waiting, running and
  deferred counts per job type. Deferred is the starvation signal and had
  no exposure anywhere; it is set when a type is at its concurrency limit.
- jobq_saturation{metric}: running tasks, worker-thread count and total
  waiting, so a slowdown spanning several subsystems can be attributed to
  worker-pool exhaustion instead of being diagnosed once per victim.

Both read through two new const accessors on JobQueue that take the
existing mutex once and copy integers, so a single reading is internally
consistent and no per-job cost is added. The job_type label reuses the
same JobTypes name helper the existing job counters use, so the two label
sets join.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 13:46:03 +01:00
Pratik Mankawde
9956b9b651 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
Resolved five files. In each case both sides had content worth keeping,
so nothing was taken wholesale:

- MetricsRegistry.cpp: kept the incoming `handler` label on the job
  instruments and re-applied this branch's `queuedDurUs >= 0` guard,
  which the incoming side does not have.
- telemetry-runbook.md: took the incoming gauge table, which adds the
  three per-job-type rows, and re-applied this branch's corrected
  `jobq_job_count` name.
- 09-data-collection-reference.md: kept this branch's validation
  inventory (newer counts, extra Config File column) and inserted the
  incoming call-site and per-job-type gauge rows plus their explanation.
- node-health.json: merged structurally rather than by text. This
  branch's panels are authoritative; only the two incoming job-queue
  panels were appended, below the existing layout. The
  `Validated Ledger Seq` panels added directly in Grafana are preserved.
- job-queue.json, ledger-data-sync.json: panel sets were identical, so
  took the incoming side for its `$handler` variable, the handler filter
  on existing queries, and the new panels. Verified no panel was lost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:06:29 +01:00
Pratik Mankawde
a27d4d65c5 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
Resolved OpenTelemetryPlan/09-data-collection-reference.md: both sides
document the same new metrics, reached by different routes. The incoming
sections came from the phase-6 edit merged forward; this branch already
documents the same surface in its own layout, with the job lifecycle
metrics and the GetObject request path under the MetricsRegistry
section rather than a separate 2a.

Kept this branch's sections to avoid duplicate entries for every metric.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:03:02 +01:00
Pratik Mankawde
a8bfc6927c Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-07-25 12:02:03 +01:00
Pratik Mankawde
49d2edfe61 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
Resolved OpenTelemetryPlan/09-data-collection-reference.md: this branch
replaced the StatsD-shaped Section 2 with OTel-native naming, so the
incoming StatsD tables and the StatsD per-job timer section do not apply
here. Kept this branch's names throughout.

The incoming per-job-type saturation gauge section is new content and is
retained, renumbered to 2.5 and rewritten for the OTel naming: bare
lowercase `jobq_<jobtype>_{waiting,running,deferred}` derived through
OTelCollector rather than the case-preserving `xrpld_`-prefixed StatsD
form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:01:58 +01:00
Pratik Mankawde
15596f5b8d feat(telemetry): pinpoint root cause of slow TMGetObjectByHash service
Slowness on the peer object-fetch path could be observed but not
attributed. Job duration metrics carry only `job_type`, and both
`RcvGetLedger` and `RcvGetObjByHash` report as `ledgerRequest`, so a
queue-wait spike could not be traced to a handler. Nothing measured
NodeStore cost, request size, or the differential charge.

Latency now decomposes into three additive parts, each separately
measurable:

    end-to-end = queue wait + NodeStore lookup + everything else

- `handler` label on job_queued_total/_started_total/_finished_total and
  job_queued_us/job_running_us. The value is sanitised: a name passes
  through only if non-empty and all ASCII letters, else "other". Two job
  names embed a ledger sequence, so a raw label would mint one series
  per ledger; the rule bounds the domain at 43 names plus "other".
- getobject_lookup_us, _request_objects, _lookups_total{result},
  _rejected_total{reason} and _charge, recorded at their call sites.
  All three histograms get explicit bucket views: the SDK default stops
  at 10,000, which every one of them exceeds.
- Per-job-type waiting/running/deferred gauges for the 35 non-special
  job types. `deferred` is the leading indicator, since addJob never
  rejects -- it defers, so backpressure otherwise shows up only as
  latency after the fact.

`JobQueue::collect()` snapshots the counters under the queue lock and
publishes gauges after releasing it. Writing them while holding the lock
would invert a lock order against the collector's own lock, which the
collector's flush thread already holds when it calls this hook.

Tests assert exact values, including that the charge is priced on the
requested count rather than the capped iteration count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 11:59:16 +01:00
Pratik Mankawde
6768aa4c07 docs(telemetry): document handler label, GetObject and queue-saturation metrics
Add reference entries for the observability surface introduced on
phase-9: the `handler` label on the job instruments, the five
`getobject_*` request metrics, and the per-job-type queue saturation
gauges.

Names here follow this branch's StatsD pipeline, which preserves case
and carries the `xrpld_` prefix, so they differ from the lowercased
OTel-native names used from phase-7 onward. The sections state where
the implementing code lives, since it is introduced downstream.

Also correct pre-existing entries: `job_count` exports as
`jobq_job_count` via the collector group prefix, the non-special job
type count is 35 (not 36), and `JtLedgerData` has five producers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 11:58:16 +01:00
Pratik Mankawde
3e2a1ea958 feat(telemetry): add ledger-acquire and SHAMap fetch diagnostics (WP-A3)
Signals that separate a sync that is merely slow from one that will never
finish:

- sync_acquire{missing_state_nodes_max, missing_tx_nodes_max, in_flight,
  received_data_depth}: how many SHAMap nodes each in-flight acquire is
  still waiting for. getMissingNodes already computed this and the callers
  discarded it after a trace log. A count that stays flat means the
  acquire is wedged; a shrinking count means it is progressing. Recorded
  once per sweep, never inside the per-node walk, and reset when a tree
  completes so a finished acquire does not read as stuck forever.
- shamap_cache_hit_rate{treenode}: hit rate of the in-memory tree-node
  cache, which sits above the node store, so it is distinct from the
  existing NuDB ratio. A cold cache on a fresh node sends every traversal
  step to disk.
- sync_acquire_no_progress_total: timer ticks where an acquire made no
  progress, previously only logged.
- sync_addnode_total{good,duplicate,invalid}: whether arriving nodes are
  useful, duplicated or rejected, so wasted fetch work is visible.
- sync_acquire_source_total{local,network}: whether a ledger was served
  from the local store or had to be fetched.

Adds getBad()/getDuplicate() to SHAMapAddNode and an acquireProgress()
accessor on InboundLedgers so the xrpld gauge can read these without
libxrpl depending on telemetry.

ledger_seq is deliberately not a metric label: it is unbounded. Per-ledger
identity stays on the ledger.acquire span; the metrics expose bounded
aggregates instead.

The full-below cache hit rate is not exported: KeyCache updates different
counters than getHitRate() reads, so it would always report zero. That
libxrpl bug is documented rather than papered over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 10:18:08 +01:00
Pratik Mankawde
7c7509d01f feat(telemetry): add sync-state diagnostics (WP-A2)
Five signals that explain why a node is not advancing toward full, none of
which were observable before:

- state_changes_total now carries {from,to} mode labels, emitted at
  setMode using the existing strOperatingMode helper. A bare count could
  not distinguish a healthy climb from a node flapping between tracking
  and connected. Removes the now-unused incrementStateChanges wrapper.
- sync_state{initial_full_duration_us}: time to first reach full, which
  StateAccounting already computed but exposed only in server_info.
- sync_state{network_ledger_gate}: whether the node is still refusing to
  build ledgers because it has no network ledger.
- sync_state{server_stall_seconds} and server_stall_events_total: how
  long the main thread has been unresponsive. LoadManager computed this
  and only logged it, so a stall was invisible until the fatal threshold.
  The episode rule is a pure function so it can be tested without adding
  a test-only mutator to LoadManager.
- sync_state{ledgers_behind}: how far our validated sequence trails the
  best sequence any peer advertises, read from already-cached peer ranges
  so no extra network traffic is added.

Also fixes the naming checker: it derived only the first label of a
multi-label instrument, so a dashboard querying the second label was
wrongly rejected.

Note: the clang-tidy hook cannot run in this worktree (no build
directory); the remaining pre-commit hooks, the naming check, dashboard
schema and harness syntax all pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:02:03 +01:00
Pratik Mankawde
188de0a5f3 feat(telemetry): add pre-quorum bootstrap sync diagnostics (WP-A1)
A freshly started node most often stalls before it ever peers or reaches
quorum, and that whole chain had no telemetry. Adds the six signals that
make it observable:

- dns_resolve_total / dns_resolve_latency_ms: configured-peer hostname
  resolution, emitted from OverlayImpl so libxrpl stays independent.
- overlay_connect_total / overlay_dial_latency_ms: outbound dial outcome
  by terminal reason, plus dial duration.
- handshake_negotiation_fail_total: protocol and network-id negotiation
  rejections, labelled by reason, so a misconfigured network is no longer
  indistinguishable from unreachable peers.
- unl_fetch_total and the unl_quorum gauge: validator-list fetch outcome
  per site and trusted key count against the required quorum. Without
  these a bad validators.txt leaves the node syncing forever with no
  signal.
- clock_close_offset_seconds: network close-time offset, which server_info
  hides below 60s but which stalls consensus participation.

Panels land in the Bootstrap row of the Ledger Sync Health dashboard, the
metrics are asserted by the workload validator, and both the reference and
the runbook flow describe them.

Levelization baseline regenerated: overlay now includes MetricMacros.h, so
the overlay/telemetry pair is reported one-way instead of bidirectional.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 07:53:20 +01:00
Pratik Mankawde
96914b9f40 feat(telemetry): scaffold fresh-node sync diagnostics (WP-A0)
Adds the anchors the sync-diagnostics signals attach to, with no signals
emitted yet:

- New "Ledger Sync Health" dashboard (uid ledger-sync-health) with the
  standard template-variable block copied from an existing board, plus
  empty "Bootstrap (Domain 0)" and "Sync pipeline" rows.
- Signal index section in the data-collection reference, an operator-flow
  stub in the telemetry runbook, and a glossary anchor.
- A sync_diagnostics group in expected_metrics.json and a matching
  assertion helper in validate_telemetry.py so CI fails when a signal
  regresses to absent.

Also registers the new dashboard uid with the harness so the board is
covered by validation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 21:06:05 +01:00
Pratik Mankawde
694062d8fd Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-07-24 16:15:41 +01:00
Pratik Mankawde
2d7f3792bf Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
# Conflicts:
#	docs/telemetry-runbook.md
2026-07-24 16:15:27 +01:00
Pratik Mankawde
380a7160c6 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-07-24 16:14:30 +01:00
Pratik Mankawde
dee90b7c01 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
# Conflicts:
#	OpenTelemetryPlan/09-data-collection-reference.md
2026-07-24 16:14:16 +01:00
Pratik Mankawde
21d9b2f8f9 docs(telemetry): add current_ledger_seq/hash to 09 attribute inventory
Document the two new ledger-correlation attributes in the data-collection
reference: add them to the transaction and TxQ attribute tables (with per-span
coverage), update the attribute count, and correct the txq.enqueue parent note
(parents to tx.process on the submission path via explicit context; a root on
the open-ledger rebuild path, correlated via current_ledger_seq).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 16:11:18 +01:00
Pratik Mankawde
f0df68ae2e docs(telemetry): add protocol span-flow diagrams to runbook
Add a "Protocol Span Flow" section to the telemetry operator runbook: 8
Mermaid diagrams that map every OTel span onto the real xrpld control flow
and XRPL protocol order (verified against code and docs/consensus.md), for
use as the canonical key when linking the span hierarchy.

- Master overview, client/peer ingress, shared apply pipeline, consensus
  round, accept/build/finalize, and pathfinding/ledger-acquire side flows.
- Every node/branch is labelled with the span that represents that state or
  transition (or explicit "(no span)"); drops/abandons are marked terminal.
- Shows real loops, retries, recovery, and drop branches: multi-round
  consensus settling (avalanche threshold rounds + MovedOn/Expired retry with
  wrong-ledger recovery), 3-pass tx apply retry, TxQ cross-ledger retry,
  quorum-gated async validation with abandoned-ledger, ingress backpressure
  drops, and cross-node context propagation.
- Adds a divergence table noting where OTel span parenting does not match the
  real protocol flow (deterministic/hash trace-id roots, JtAccept/JtUpdatePf
  job hand-offs, sequential peer->consensus receive stages).

Replace the stale planning-era diagram in OpenTelemetryPlan/08-appendix.md
(which named spans that were never built) with a pointer to the runbook.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 19:42:13 +01:00
Pratik Mankawde
48a5aad91d Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
Brings the coroutine-aware OTel context storage refactor forward to phase-10:
CoroAwareContextStorage + install, ScopedSpanGuard same-store assertion,
non-owning ScopedActivation, scoped rpc.command spans, tx/consensus/ledger
worker-body activation, coro-store-swap tests, and the M1/M2 hardening.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 23:16:18 +01:00
Pratik Mankawde
28a8ab144b feat(telemetry): correlate ledger.acquire outcome log via span activation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:58:17 +01:00
Pratik Mankawde
68bf633955 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
Brings coroutine-aware context storage + tx/consensus worker-body activation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:48:16 +01:00
Pratik Mankawde
31f6eee983 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation
Brings coroutine-aware context storage + tx/consensus worker-body activation.
The coro-aware storage keeps Log.cpp's ambient-context trace-id stamping correct
across coroutine yields, so RPC/tx/consensus log lines stay trace-correlated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:47:45 +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
e0c4272f5b docs(otel): coro-aware context storage; RPC/tx/consensus log correlation retained
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 22:40:13 +01:00
Pratik Mankawde
1fc65db139 docs(telemetry): rename Ripple->XRPL / rippled->xrpld in telemetry docs
The check-rename CI gate requires all prose/comment references to use the
new naming. Fix the four remaining occurrences ("Ripple epoch",
"rippled's doAccept") introduced by this branch's dashboard/glossary
commits. Comment- and doc-only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 15:05:29 +01:00
Pratik Mankawde
0e02a67e81 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-07-22 13:46:45 +01:00
Pratik Mankawde
dd96d8ad34 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill 2026-07-22 13:28:44 +01:00
Pratik Mankawde
dad2dc2acb Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-07-22 13:28:24 +01:00
Pratik Mankawde
cef7a8b16c Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
# Conflicts:
#	OpenTelemetryPlan/09-data-collection-reference.md
2026-07-22 13:28:15 +01:00
Pratik Mankawde
2f627e8e8e docs(telemetry): 09-doc — pathfind.request parents to rpc.process, not rpc.command
Reflects the C1 fix: rpc.command.* stays unscoped (its dispatch wraps
doRipplePathFind which yields), so pathfind.request nests under rpc.process.
The request -> compute -> discover sub-tree nests correctly via ScopedSpanGuard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 13:12:55 +01:00
Pratik Mankawde
f52673b6c1 feat(telemetry): correct ledger-close-time derivation on dashboards
The "ledger close time" was mis-derived and partly un-queryable:

- Build vs Close Duration derived close time from the consensus.ledger_close
  span, which only wraps the onClose() prologue (~0.8ms live) — not the close.
  Repoint the close series to consensus.round (full round, live P95 ~4.8s).
- The network close-time value (close_time) lived only as a span attribute,
  un-queryable in Prometheus and unfit as a spanmetrics label (monotonic
  timestamp -> unbounded cardinality). Expose it as last_close_time on the
  existing server_info observable gauge (native gauge, no new instrument).
- Add a "Ledger Close Interval & Age" panel to ledger-operations and
  node-health: interval = 1/rate(ledgers_closed_total) (counter-based,
  scrape-independent); age = time() - (last_close_time + epoch_offset).

A gauge delta is deliberately NOT used for the interval — a timestamp gauge's
delta aliases to the scrape period, not the close cadence (verified live).
Guardrail comments in both collector configs record why close_time must never
become a spanmetrics dimension. Docs (09-reference) and the operator runbook
updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 12:23:21 +01:00
Pratik Mankawde
f78bc433b0 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-07-22 10:58:55 +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
7bdc14eee5 docs(telemetry): 09-doc — deterministic roots are true roots; rootSpan->freshRoot
Document that deterministic-trace_id spans (tx.* apply pipeline, tx.process,
tx.receive, consensus.round) are now genuine trace roots with empty
parent_span_id via the custom DeterministicIdGenerator, superseding the old
synthetic-parent behavior that showed 'root span not yet received' in Tempo.
Also update the fresh-root note: peer entry spans use ScopedSpanGuard::freshRoot().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 10:58:35 +01:00
Pratik Mankawde
ce3d806310 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
Brings the 09-data-collection-reference doc-sync (rpc_in_flight_requests entry)
forward from phase9.
2026-07-21 20:34:12 +01:00
Pratik Mankawde
42615355be docs(telemetry): add rpc_in_flight_requests to metric-surface reference (09-doc) 2026-07-21 20:33:56 +01:00
Pratik Mankawde
f569440bd5 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-07-20 12:19:59 +01:00
Pratik Mankawde
de49319b31 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill 2026-07-20 12:19:45 +01:00
Pratik Mankawde
6203e46e8d Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-07-20 12:19:37 +01:00
Pratik Mankawde
4908b82077 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-07-20 12:19:28 +01:00
Pratik Mankawde
56c03ff24e Merge branch 'pratik/otel-phase5-docs-deployment' into pratik/otel-phase6-statsd
# Conflicts:
#	src/xrpld/overlay/detail/PeerImp.cpp
2026-07-20 12:19:11 +01:00
Pratik Mankawde
777aa9b467 Merge branch 'pratik/otel-phase4-consensus-tracing' into pratik/otel-phase5-docs-deployment 2026-07-20 12:17:52 +01:00
Pratik Mankawde
c3b1f69ba8 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing
# Conflicts:
#	include/xrpl/telemetry/SpanGuard.h
#	src/xrpld/app/consensus/RCLConsensus.h
#	src/xrpld/consensus/ConsensusTypes.h
2026-07-20 12:10:44 +01:00