Commit Graph

706 Commits

Author SHA1 Message Date
Pratik Mankawde
bf5c3e328c ci(telemetry): make a cluster bring-up failure diagnosable
A validation run timed out at Step 3 with only 4 of 5 nodes proposing, and
the reason was unrecoverable afterwards. Two gaps caused that.

The node-log artifact collected `node*/debug.log` but not `node*/stdout.log`.
A node that dies before its log sink opens never writes a debug.log at all,
so stdout is the only place its reason survives — and that file is written by
the harness and read by nothing, so it went to the runner and was discarded.
The failing node's log was simply absent from the artifact.

The readiness loop also fetched each node's `server_state` and threw it away,
reporting only a count. "4/5 nodes proposing" says a node is missing but not
which one, so there is nothing to grep for even once the logs are kept. The
timeout now names each node that is not proposing along with the state it
last reported, distinguishing a node that answered with a non-proposing
state from one whose RPC port did not answer at all.

Neither change affects a healthy run: the accumulator resets each attempt and
stays empty while every node is proposing.
2026-08-14 22:43:03 +01:00
Pratik Mankawde
2e1ebf90bb docs(telemetry): describe the Cloud alert path without the helper script
The alerting example env file, the contact-point provisioning header and one
runbook line still pointed at a gitignored helper script and at a rollout
phase number, neither of which ships. The contact-point header now states the
policy-tree warning inline rather than deferring to a file the reader cannot
open.
2026-08-14 22:40:53 +01:00
Pratik Mankawde
84536ef25f docs(telemetry): name the workload stack instead of its phase number
The health-check note identified the stack by a rollout phase number
defined only in a planning folder outside the shipped tree.
2026-08-14 22:37:33 +01:00
Pratik Mankawde
99ae7cfb5f Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
Four files conflicted.

xrpl.proto and TraceContextPropagator.h: phase-9's note is a semantic
superset of phase-8's and already carries no plan-folder pointer, so
phase-9's text is kept and nothing phase-8 said is lost.

node-health.json: the only change phase-8 makes to this file is renaming a
row that phase-9 had already deleted, so phase-9's file stands. Panel count
holds at 59 and both Validated Ledger Seq panels survive.

TESTING.md: resolved per hunk rather than by side. Phase-9's replacement of
the drifted span table and its new Grafana Cloud section are kept, and
phase-8's de-numbered "Test 3: Log-Trace Correlation" heading is taken --
keeping phase-9 wholesale would have reinstated the phase number that
phase-8 removed.
2026-08-14 22:36:14 +01:00
Pratik Mankawde
ca19c456fd test(telemetry): name the metrics under test instead of plan task numbers
The integration-test sections, alert provisioning headers and a naming-check
test comment were indexed by rollout phase and task numbers defined only in
planning documents outside the shipped tree. Each now names what it covers.
2026-08-14 22:26:26 +01:00
Pratik Mankawde
aea0422562 fix(telemetry): count only rendered panels, and drop no-reply latencies
Two defects reported against the validation harness. Both premises were
correct, but neither suggested fix was, so the remedies differ.

Dashboard panel count: `len(dashboard["panels"])` treated Grafana row
objects as panels and skipped the panels nested inside collapsed rows, so
every dashboard was over-reported by between 1 and 10 (`log-derived-insights`
read 41 against a true 31). The check also passed unconditionally on HTTP
200, so a dashboard that renders nothing would still pass. `_leaf_panel_count`
now walks row children and the result gates the verdict. Gating on the old
top-level length, as suggested, would not have caught the case it was aimed
at: a dashboard made only of collapsed rows counts its rows and reports a
positive number while rendering nothing.

RPC latency percentiles: `LoadStats.record` appended a latency for every
outcome, including requests that never got a reply, where the value is a
time-to-failure rather than a round trip. A timeout contributed the full
receive timeout, and at the error rate a real run shows this reported p95 and
p99 of 10000 ms where the true figure was 5 ms. `record` now takes an
optional latency and the timeout path passes none. The suggestion to append
only on success was not adopted: a reply carrying `status: error` is a
completed, timely round trip whose latency is a genuine measurement, and
discarding it would throw away real data. `per_command` is now keyed off the
request counts rather than the latency map, so a command whose every request
timed out still appears in the report instead of vanishing from it, and each
entry carries a `latency_samples` count.
2026-08-14 21:58:43 +01:00
Pratik Mankawde
173e76556e fix(telemetry): stop the mainnet node colliding with devnet and drop signing
The mainnet and devnet telemetry configs are host processes sharing one
network namespace and working directory, and they agreed on every port and
both database paths. Running them together left the second unable to bind,
and running them in sequence pointed a mainnet node at a store holding the
other network's ledgers, silently and with no error.

Offset the mainnet ports by ten and name both database paths after the
network, matching what debug_logfile already did.

Also narrow the attack surface this config carried. [port_ws_public] has
no admin key, so every caller on it is a guest, and it was bound to all
interfaces on a node following Mainnet; it is now loopback, and nothing in
the repo connects to that port. [signing_support] is removed: it only
affects non-admin callers, the admin ports already grant signing to local
callers, and upstream deprecates the commands it exposes. Verified against
a running node: signing still succeeds on the admin port and is refused as
notSupported on the public one.
2026-08-14 21:57:54 +01:00
Pratik Mankawde
8a6ecb8093 fix(telemetry): carry the work item through dashboard aggregations
Every aggregation that filtered on xrpl_work_item left it out of its
sum by() grouping, so PromQL dropped the label and the xrpl_ident legend
these panels build from it rendered without the work item. 217 clauses
across the touched boards.

Perf-iac gives each work item its own set of nodes, so service_instance_id
already separates the runs and the visible effect is the legend rather
than merged series. Four clauses are the exception and aggregate across
nodes, where the grouping does real work: Convergence, Lag Behind Network
Tip and Build Version now measure spread, lag and version distribution
within one work item instead of smearing two independent clusters
together. Lag Behind Network Tip also needed its on() join key extended to
match, or two coexisting work items would fail the query outright.

The five heatmap inner by (le) clauses are deliberately untouched: an
extra grouping label would superimpose several distributions.

Also in this change:
- transaction-overview gains per-type-per-stage rate and failure-rate
  panels, so all three requested dimensions exist rather than latency
  alone, and its stage-rate panel picks up the filters and legend its
  siblings already had.
- The acquire-duration panel splits by outcome. An aborted acquisition is
  open until the sweep reaps it, so mixing it into one percentile
  conflated fetch latency with time-to-abandon.
- Ledger Acquire Rate by Outcome gets the same Title Case value mapping as
  its neighbour, and node-health gains the $outcome variable the two
  panels now need.
2026-08-14 21:57:50 +01:00
Pratik Mankawde
8781265084 fix(telemetry): keep the work item in the two phase-9 sync aggregations
LedgerReq Wait by Handler and NodeStore Read Latency (Bottleneck
Discriminator) filter on xrpl_work_item but left it out of their sum by()
grouping, so PromQL dropped the label and the xrpl_ident legend lost that
segment.

These two targets took their current form on this branch, so they are
fixed here. The other nine in this file predate it and were fixed on
pratik/otel-phase7-native-metrics, which merges forward.

The discriminator panel divides two aggregations; both sides carry the
label so their label sets stay equal for vector matching.
2026-08-14 21:47:57 +01:00
Pratik Mankawde
292aee922a Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-08-14 21:47:42 +01:00
Pratik Mankawde
004b8e92ab fix(telemetry): keep the work item in ledger-data-sync aggregations
Nine targets across four panels filtered on xrpl_work_item while leaving
it out of their sum by() grouping. PromQL keeps only the labels listed in
by(), so the label was dropped from the result and the xrpl_ident legend
these panels build from it lost that segment.

Perf-iac runs stamp xrpl_work_item and give each work item its own set of
nodes, so service_instance_id already separates the runs; the visible
effect is the legend rather than merged series. Adding the label makes the
work item identifiable, which is the comparison these panels exist for.

NuDB Read Latency and NuDB Read Found Ratio each divide two aggregations.
Both sides get the label so their label sets stay equal and vector
matching still works.
2026-08-14 21:47:20 +01:00
Pratik Mankawde
19f49cf961 docs(telemetry): explain the telemetry dependency loop and the Compose floor
Two comment-only notes.

MetricsRegistry.cpp: record why the app and overlay includes exist and
what they cost. They are what makes loops.txt carry
xrpld.app <-> xrpld.telemetry and xrpld.overlay <-> xrpld.telemetry, where
ordering.txt previously had telemetry strictly below both. The observable
gauges are pull-model, so their callbacks need the concrete types to
sample live state. The cycle is confined to this translation unit: no
telemetry header includes app or overlay, and all of src/xrpld builds
into one target, so there is no header or link cycle. Inverting it needs
a metrics-source interface below overlay, which is left as follow-up.
Also note loops.txt is generated and must never be hand-edited.

docker-compose.yml: state the Compose >= 2.24.0 floor. The grafana
service uses the long-form env_file mapping, which older Compose cannot
parse, and it fails for the whole file rather than that one service. The
long form is required because .env.alerting is gitignored and absent in a
fresh clone, and the short form treats a missing env file as an error.
2026-08-14 21:45:09 +01:00
Pratik Mankawde
46ed205790 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-08-14 21:21:28 +01:00
Pratik Mankawde
be8f987b39 docs(telemetry): drop the phase number from a node-health row title
The row was titled after the rollout phase its metrics came from, a number
defined only in a planning folder outside the shipped tree. Grafana shows
this title to operators, so it named something no reader could look up.
2026-08-14 21:21:25 +01:00
Pratik Mankawde
3a91a7414e Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics
# Conflicts:
#	docker/telemetry/grafana/dashboards/statsd-rpc-pathfinding.json
#	docker/telemetry/integration-test.sh
2026-08-14 21:20:39 +01:00
Pratik Mankawde
974835589a docs(telemetry): drop the plan task reference from a StatsD panel note
Two panel descriptions pointed at a rollout phase and task number defined
only in a planning folder outside the shipped tree. The note still names
the file and the change it is waiting on, which is the part a reader can
act on.
2026-08-14 21:19:49 +01:00
Pratik Mankawde
8670e31d6c docs(telemetry): drop rollout phase numbers from the span catalog
The catalog tagged every span with a rollout phase number defined only in
a planning folder outside the shipped tree, so the column meant nothing
to a reader of the repository. The span name and source file identify
each entry.
2026-08-14 21:17:02 +01:00
Pratik Mankawde
68b94e625c docs(telemetry): name log-trace correlation instead of its phase number
These comments and headings identified the feature by a rollout phase
number defined only in a planning folder outside the shipped tree, so the
label meant nothing to a reader of the repository.
2026-08-14 21:13:59 +01:00
Pratik Mankawde
2bac88e67c test(telemetry): label integration-test sections by what they verify
The section labels carried rollout phase numbers that are defined only in
a planning folder outside the shipped tree, so they meant nothing to a
reader of the repository. The descriptive half of each label already
identifies the section.
2026-08-14 21:11:54 +01:00
Pratik Mankawde
c4888dd066 test(telemetry): label the native-metrics section by what it verifies
The label carried a rollout phase number defined only in a planning
folder outside the shipped tree. The descriptive half already identifies
the section.
2026-08-14 21:11:14 +01:00
Pratik Mankawde
2770dbbf11 docs(telemetry): drop the legacy daemon name from the workload README
The rename check rewrites a bare pre-rename binary name in any processed
doc, which turned the sampler's selector description into "against xrpld
or xrpld". Describe the fallback without spelling the legacy token.
2026-08-14 20:23:11 +01:00
Pratik Mankawde
d059f21bf3 fix(telemetry): address review findings in the workload validation harness
Fixes the review findings on this PR that belong to files it owns, plus
several defects found while verifying those fixes. Findings in files owned
by upstream branches are routed there and left untouched here.

Correctness:
- tx_submitter: advance the account sequence only on results that actually
  consume one (tes*, tec*, terQUEUED). tem*/tef*/tel* never reach the
  ledger, so advancing left a permanent gap that every later submit from
  that account inherited. Add a re-fetch hatch so a repeated non-consuming
  failure cannot livelock on the same sequence, and gate the account check
  on funded-ness rather than list length.
- validate_telemetry: filter spans by name before collecting attributes, so
  a per-span attribute contract can no longer be satisfied by a sibling
  span; require exact name equality for non-wildcard children and glob
  matching for wildcards; bounds-check every returned series instead of
  only the first.
- collect_system_metrics: select xrpld by argv[0] rather than a substring
  match on the whole command line, which averaged in unrelated processes
  and reported their RSS as xrpld's. Count genuine 0.0 CPU readings, use a
  clamped nearest-rank p99 index, and record RPC latency only on success.
- benchmark: return each verdict through a named variable instead of a
  command substitution, so the pass/fail counters survive and the exit gate
  can fire. Scale before dividing in the percentage math, which truncated a
  1.26% impact to 1.00% and cleared a 1% threshold.
- compare_to_baseline: fall back to the absolute bound when the baseline is
  not positive, so a 0 -> 500 ms jump is no longer "within bounds".
- rpc_load_generator: bound each connection to one in-flight recv(), drain
  in-flight requests before closing, use a nearest-rank percentile, and
  report delivery shortfall so an under-delivered run cannot pass with a 0%
  error rate.

Fail loudly instead of silently:
- run-full-validation: treat a consensus timeout and a missing validated
  ledger as fatal infrastructure errors, and fold the orchestrator and
  benchmark exit codes into the final status. A degraded cluster previously
  ran a full validation pass and reported misleading downstream failures.
- collect_system_metrics: warn per empty measurement source, emit
  metrics_complete, and exit non-zero instead of substituting zeros that
  pass every threshold. Require GNU date with %N rather than falling back
  to a per-sample python3 fork that costs more than the threshold it is
  measured against.
- benchmark: distinguish "could not measure" from "exceeded thresholds",
  install a cleanup trap so a failure cannot leak nodes and ports, and
  report an unusable baseline as inconclusive.
- workload_orchestrator: bound subprocess communicate() and fail the exit
  gate on per-phase errors.

Also pins the workload compose images to the versions the sibling stack
already uses, hash-pins the Python dependencies, restricts the validator
config template to loopback, corrects the dashboard and metric counts in
the reference docs, drops a span from the regression gate that cannot fire
under a WebSocket-only workload, and narrows the teardown pkill pattern so
it no longer matches processes that merely mention the work directory.

Verified with a full harness run against a local five-node cluster:
158 of 158 checks passed with no regressions detected.
2026-08-14 19:59:19 +01:00
Pratik Mankawde
d3ff79121d fix(telemetry): assert histogram metrics by their exported names
The Telemetry Validation workflow failed with three "0 series" checks:
rpc_method_us, job_queued_us and job_running_us. All three are Histograms,
and the Prometheus exporter emits a histogram only as the
_bucket/_count/_sum triple -- the bare instrument name is never a series,
so validate_metrics() could never match it.

Evidence from the failing run (31804450127): its own metric-name dump
lists rpc_method_us_bucket/_count/_sum and no bare rpc_method_us, while
the sibling counters recorded in the same function bodies passed with 100
and 67 series. capture_timings.py, which queries job_queued_us_bucket and
job_running_us_bucket, returned real values for the acceptLedger job type
in that same run. Every one of the 10 histograms present exposes the full
triple, so all three suffixes are safe to assert.

Name them the way the exporter does, matching what the spanmetrics group
above already does for span_duration_milliseconds and what
regression-metrics.json and the job-queue dashboard already query. The
metrics stay in their asserted groups because they are genuinely
unconditional, so `not_asserted` would be wrong.
2026-08-14 15:23:20 +01:00
Pratik Mankawde
1dea579d51 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-14 12:36:03 +01:00
Pratik Mankawde
e6d4137c9e docs(telemetry): replace the non-existent default CMake preset
Every telemetry build instruction told the reader to run
`cmake --preset default`. No `CMakePresets.json` is tracked in the repo, and the
only preset that exists is `conan-release`, generated into the gitignored
`CMakeUserPresets.json` by `conan install`. So the documented first build step
fails before it starts, in the runbook's quick-start, its disable procedure, and
the telemetry stack's testing guide.

Replaced with the flow BUILD.md actually documents -- `conan install ..
--output-folder .` followed by an explicit `conan_toolchain.cmake` invocation --
with `-o telemetry=True` / `-Dtelemetry=ON` as the telemetry delta, and a note
that `--preset conan-release` works as a shorthand.
2026-08-14 12:35:59 +01:00
Pratik Mankawde
22e440aee1 fix(telemetry): correct the phase-10 validation harness against the code
The harness manifests asserted things the code cannot produce and missed most
of what it does. Two assertions were failing every run, and the metric set
covered 16 of the ~41 emitted names.

expected_spans.json: rpc.process was required with rpc.ws_message as its
parent, but it is created only in ServerHandler::processRequest() on the HTTP
path, so a WebSocket-only workload never produces it -- it is now optional and
parented to rpc.http_request, and the rpc.process -> rpc.command.* edge is
skipped with the real reason instead of a coroutine-context-loss diagnosis that
was never the cause. Adds the missing rpc.ws_upgrade span, corrects four
parents (consensus.mode_change, pathfind.request, and update_positions/check,
which are children of consensus.establish rather than consensus.round), and
demotes conditionally-set attributes out of required_attributes so a healthy
run stops failing. Counts recomputed from the file: 41 span types, 62 unique
required attributes.

expected_metrics.json: 16 -> 52 asserted entries across the job-queue, RPC
method, reduce-relay, overflow and validation families, plus the fifteenth
dashboard uid. Metrics the harness workload cannot exercise -- erroring RPC,
ledger-mismatch, TxQ overflow, and the lazily-created getobject_* instruments
-- are listed in a not_asserted group the validator skips, rather than as
assertions that would fail on a healthy node.

The workflow's push trigger listed two globs matching nothing
(include/xrpl/basics/Telemetry*.h, src/xrpld/app/misc/Telemetry*), so no C++
telemetry change ever triggered validation. Replaced with the paths the code
actually lives in, including src/libxrpl/beast/insight/** for the insight
export path the harness depends on. The four inert workflow_dispatch inputs are
now labelled UNUSED rather than looking like working knobs.

Docs: the workload README described a StatsD dirty-flag mechanism under a
member name that does not exist, on a code path the harness never uses -- it
sets [insight] server=otel, so gauges export through an observable-gauge
callback every cycle. Adds the missing txq-burst phase, reconciles three
different dashboard counts, and drops "posts summary to PR", which the workflow
has no permission to do. The runbook's phase-10 section loses the last
sampling_ratio reference (not a config key), gains a Regression Gate and CI
subsection covering the gate that can fail CI, and its compose-logs command now
names the workload compose file. cmake --preset default is left for a separate
change: no CMakePresets.json is tracked, so it is wrong everywhere it appears.

Also drops the dead exporter=otlp_http key the harness wrote into every node
config, and stops capture_timings.py defaulting --profile to a profile that
does not exist.
2026-08-14 12:34:33 +01:00
Pratik Mankawde
0825ac8ecc Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
Three doc conflicts, all inside Phase 10's own sections. Resolved by keeping
phase-9's corrections and re-applying phase-10's ownership of those sections on
top, rather than taking either side wholesale:

06 §6.8.3 — Status now says Phase 10 is implemented on THIS branch (phase-9's
copy said "not merged into this branch", true there, false here). The stale
"71 checks" enumeration and "Current Status" list are replaced by phase-9's
dynamic-count description plus a Known Gaps list that states the real reason
the rpc.process hierarchy is unassertable: rpc.process is HTTP-only, so a
WebSocket-only workload never produces it. Phase-9's CI Deliverable subsection
is carried through; exit criteria keep phase-10's tick state with phase-9's
corrected wording.

06 §6.8.3 Architecture — the automatic merge had resurrected phase-10's stale
2-node cluster prose and diagram over phase-9's 5-node correction. Reconciled
to phase-10's topology (native xrpld processes vs the containerised backend,
which is accurate) with phase-9's node count: 5 validators, and the collector
labelled OTLP + filelog rather than StatsD, which the config has never had.
Dropped the "all 26 metrics required" label in favour of the manifest.

06 §6.8.3 Key Implementation Details — two claims corrected against the code.
The StatsD m_dirty gauge fix describes a member that exists nowhere in the
repo, and the harness sets [insight] server=otel anyway, so gauges export
through an observable-gauge callback. The tx.receive attribute keys are bare
suppressed and tx_status, not dotted, and tx_status is set only on the
reject/known-bad/dropped paths, so it is absent on a successful receive.

09 §5c — kept phase-10's four-column table shape, with phase-9's corrected
counts: 40 of 41 emitted spans, 67 required attributes, 14 of 15 dashboards.

Phase10_taskList — dynamic inventory totals, the real RPC span trees, and exit
criteria ticked where the code on this branch closes them. Per-RPC timings are
recorded as not gated: regression-metrics.json defines only spans and job_queue.

Verified every hunk of the phase-10 diff falls inside a Phase-10-owned section
(06 §6.8.3, 09 §5c, Phase10_taskList, and the runbook's appended Phase 10
sections); no phase-9-owned text is modified from this branch.
2026-08-13 19:02:17 +01:00
Pratik Mankawde
3153f3ef56 docs(telemetry): align runbook and plan docs with the shipped phase-9/10 code
The reference docs had drifted from the code in ways that break the reader
rather than merely misinform: PromQL examples that return no data, a rollback
flag that is a no-op, a sampling knob that does not exist, and two span parents
that moved. Code is treated as the truth throughout; where the code is the
defective side, the doc now records it as a known issue instead of describing
the bug as intent.

Renames the docs missed: histogram names gain the exporter's unit suffix
(ios_latency_milliseconds_bucket and four siblings), ledger_history_mismatch
gains _total, the StatsD-era quantile label gives way to le buckets,
rpc.request becomes rpc.http_request, traces_spanmetrics_calls_total becomes
span_calls_total, and the nine dotted xrpl.* span attributes are recorded as
renamed rather than left as live keys.

Re-parenting: consensus.update_positions and consensus.check are children of
consensus.establish, not of consensus.round.

Units and labels: state_accounting_*_duration is microseconds, not seconds;
cache_metrics label values are case-sensitive; object_count carries demangled
C++ type names. Nodestore read and write latency stays microseconds -- the
nanosecond accumulator change did not move the exported unit.

Adds what shipped but was undocumented: the ledger.acquire span, seven
consensus.round events, twelve span attributes, node_writes_duration_us, the
7-day validation-agreement window, the TxQ admission and reduce-relay metric
families, metrics_endpoint, and the phase-10 validation workflow.

Corrects claims that never held: 10% head sampling (it is fixed at 100%),
configurable redaction (it is unconditional), -DXRPL_ENABLE_TELEMETRY=OFF
(the flag is -Dtelemetry=OFF, default ON), FindOpenTelemetry.cmake and the
xrpl_telemetry target (neither exists), Promtail and a StatsD exporter in the
pipeline (neither exists), and Loki stream selection on job= (only
service_name is a stream label).

Phase 9 is marked complete, its provisioned alerting is attributed to the
branch that shipped it, and Phase 11 stays at zero except the one prerequisite
its code closes. Counts are reconciled repo-wide: 41 emitted span families,
15 dashboards on disk with 14 asserted, 13 alert rules in 5 groups.

Hardens the gate that let this drift through: Rule E of the naming check now
covers the reference docs, its allow-dotted marker is key-scoped and warns on
stale or empty use, a missing checked file is reported instead of silently
skipped, the test suite runs in CI, and doc paths trigger the check.

C++ and CMake changes are comment-only: three MetricsRegistry instrument names,
eight OTelCollector claims of a metric-name prefix that formatName never adds,
and the telemetry option's inverted default.
2026-08-13 18:55:32 +01:00
Pratik Mankawde
57d12686ea Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-11 16:28:17 +01:00
Pratik Mankawde
733af97ce3 docs(telemetry): fix peer disconnect panel regex; document overlay gaps
The Peer Disconnect Rate By Reason panel anchored its LogQL capture on
"\] ", which only matches a reason logged immediately after the [NNN]
peer-id prefix. PeerImp does not log that way: PeerImp::fail emits
"[NNN] <name> failed: <reason>" and the clean teardown emits
"close: Closed". Only ConnectAttempt::fail, which logs the bare reason,
ever matched. The panel's Timeout series was therefore connect-attempt
timeouts only, Ping Timeout was invisible, and PeerImp's own Closed was
uncounted.

Match all three prefixes and separate Ping Timeout from Connect Timeout.
Recorded as LogQL trap 11 in the runbook, alongside the other silent
failures this dashboard exposed.

Also document six overlay observability gaps found while auditing what
ping/pong and gossip traffic is actually tracked. All are pre-existing
and none is fixed here: the code fixes belong in develop-owned overlay
files (TrafficCount, OverlayImpl, PeerImp, PeerfinderManager), not on a
telemetry branch, and one of them needs a public signature change.

- 09 section 6: six known issues, each marked NOT IMPLEMENTED with
  file:line evidence -- mtCLUSTER counted as unknown (overhead_cluster_*
  always zero, 8 panels flatline), squelch_ignored byte counts always
  zero, inbound/outbound byte-basis asymmetry plus a stale Total header
  comment, ping/endpoints instrumentation absent, peer span coverage,
  and PeerFinder exporting 2 of ~17 available readings.
- 02 section 2.3.2: add a Status column to the span catalog. Of 36
  catalogued spans, 16 are live, 15 were never built, and 5 shipped
  under different names (consensus.phase.establish became
  consensus.establish, ledger.close became consensus.ledger_close,
  rpc.request split into rpc.http_request/rpc.ws_message, txq.apply
  became txq.apply_direct/txq.accept_tx). The catalog is a design
  inventory; 09 section 1.1 remains authoritative for what emits.
- Phase9_taskList: tasks 9.14-9.17 tracking the deferred work, with
  exit criteria checked only for what is actually done.
- Glossary: new Ping / pong keepalive term distinguishing ping timeout
  from connect timeout. Correct the Cluster and Squelch entries, which
  described behaviour the metrics cannot show.

The glossary header pointed at tasks/telemetry_terms.py as its
generator. That file is in no branch and nowhere on disk -- tasks/ is
gitignored one directory up -- so the header now states the file is
hand-maintained and gives the entry format.

Gates: check_otel_naming.py passes all 9 rules (Rule D over 555
dashboard queries, Rule E over the runbook); 19 doc anchors verified;
dashboard JSON valid with a one-line diff. No C++ changes.
2026-08-11 16:05:21 +01:00
Pratik Mankawde
7caff8b9d4 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-07 13:54:01 +01:00
Pratik Mankawde
74a0c759c8 fix(telemetry): trim the Read This First panel to ten rows
The debug-log notice on Log-Derived Insights was two grid rows taller than its
content needs, pushing the first data row further down than necessary. Rendered
the panel to confirm the markdown still fits with no clipping.

It is the first panel in the dashboard, so no other panel's stored y needed to
change - Grafana's vertical compaction closes the gap.
2026-08-07 13:53:39 +01:00
Pratik Mankawde
91e6619a81 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-07 12:55:56 +01:00
Pratik Mankawde
5d3ec45f23 fix(telemetry): stop repeating panels stranding their neighbours
Carries the layout fix to the dashboards this branch owns. A repeating panel
claims the whole row once it expands per network, so the non-repeating panel
paired beside it was pushed down while keeping its stored x=12 - rendering on
the right with an empty gap on its left.

Repeating panels now get a row to themselves, keeping w=12 so their copies still
tile two across. Single-value panels are grouped to the top of each row section
so the charts that follow pair with each other rather than being separated by an
interleaved repeat.

Verified against origin/phase9: no panel lost, every targets block unchanged,
ids 1..N, no overlaps, no rows with a left-hand gap.
2026-08-07 12:55:44 +01:00
Pratik Mankawde
101cacc335 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
# Conflicts:
#	docker/telemetry/grafana/dashboards/consensus-health.json
#	docker/telemetry/grafana/dashboards/node-health.json
#	docker/telemetry/grafana/dashboards/rpc-performance.json
#	docker/telemetry/grafana/dashboards/transaction-overview.json
2026-08-07 12:55:31 +01:00
Pratik Mankawde
e9ce861add Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-08-07 12:54:35 +01:00
Pratik Mankawde
97089a3b6f fix(telemetry): stop repeating panels stranding their neighbours
A repeating panel expands into one copy per network at view time and, with
maxPerRow=2, claims the whole row. The non-repeating panel paired beside it was
pushed to the next line but kept its stored x=12, so it rendered on the right
against an empty gap.

Two changes to how the layout is planned:
  - a repeating panel gets a row to itself. It keeps w=12, so its copies still
    tile two across inside that row.
  - single-value panels are grouped to the top of each row section, so the
    charts that follow pair with each other instead of being split up by an
    interleaved repeat. Without this the gaps just become wasted half-rows.

Verified per dashboard: no panel lost, every targets block byte-identical, ids
1..N, no overlaps, and no row left with a gap on its left.
2026-08-07 12:54:25 +01:00
Pratik Mankawde
bd07a681bc Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-07 12:39:41 +01:00
Pratik Mankawde
ed43121f13 fix(telemetry): give the GetObject heatmap the options its plugin requires
The GetObject Request Size Distribution heatmap, added on this branch, was
missing calculate, color and cellGap. Grafana's heatmap plugin needs them, and
without them the whole dashboard opens with "An error occurred within the
plugin" instead of rendering.

Same values as the other four heatmaps fixed upstream, taken from the one that
renders correctly on Grafana Cloud. The panel keeps its own axis label and unit.
2026-08-07 12:39:27 +01:00
Pratik Mankawde
92a205c59c Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill 2026-08-07 12:38:54 +01:00
Pratik Mankawde
76d440aa40 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-08-07 12:38:46 +01:00
Pratik Mankawde
e84b9aada0 fix(telemetry): give heatmap panels the options the plugin requires
Every heatmap carried only `tooltip` and `yAxis`, missing `calculate`, `color`
and `cellGap`. Grafana's heatmap plugin treats those as required, and without
them the panel fails to initialise: the dashboard opens with "An error occurred
within the plugin" rather than a chart.

The option values are taken from the one heatmap in this stack that does render
on Grafana Cloud (ledger-sync-health): calculate=false since the queries already
return histogram buckets, the Turbo 64-step scheme, and cellGap=1. Each panel
keeps its own yAxis label, unit and tooltip settings.

This is a long-standing defect rather than fallout from the recent layout work -
the same options are absent in origin/phase9 and in the cloud copies that were
already live.
2026-08-07 12:36:22 +01:00
Pratik Mankawde
672a56c55a Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-07 12:27:12 +01:00
Pratik Mankawde
f6df42527f fix(telemetry): taller panels for the phase-9 dashboards
Carries the sizing fix through to the dashboards this branch owns, after the
live Cloud copies showed charts clipping their legends at h=8 and stats too
cramped at h=4.

Every visualisation is h=10; tables and logs h=12; the log-derived-insights
instruction banner h=12 for its prose. A single height per visualisation also
means any two panels pair cleanly side by side.

Repeat stays on single-value panels only. The five state-timeline and heatmap
panels that had picked it up in an earlier pass are cleared, since repeating a
timeline broke ledger-data-sync on Cloud.

Verified against origin/phase9: no panel lost, every targets block unchanged,
ids 1..N, no grid overlaps.
2026-08-07 12:27:03 +01:00
Pratik Mankawde
70280e044c Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
# Conflicts:
#	docker/telemetry/grafana/dashboards/consensus-health.json
#	docker/telemetry/grafana/dashboards/ledger-data-sync.json
#	docker/telemetry/grafana/dashboards/node-health.json
#	docker/telemetry/grafana/dashboards/rpc-performance.json
#	docker/telemetry/grafana/dashboards/transaction-overview.json
2026-08-07 12:26:48 +01:00
Pratik Mankawde
a1919a0233 Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-08-07 12:26:01 +01:00
Pratik Mankawde
b226a34be4 fix(telemetry): taller panels, and stop repeating timeline and heatmap panels
Two problems showed up once these dashboards were live on Grafana Cloud.

Panels were too short. Charts at h=8 clipped their legends mid-row, and stats
at h=4 were cramped. Every visualisation is now h=10, with tables and logs at
h=12; the log-derived-insights instruction banner keeps h=12 for its prose.
Uniform height also means any two panels can pair side by side.

Repeat on a state-timeline broke the dashboard outright: ledger-data-sync
failed to open on Cloud at v45 and had to be restored to v44. Repeat is now
limited to single-value panels (stat, gauge, bargauge, table). Charts show
their networks as separate series instead, which is what a chart is for.

Repeat was also sticky: normalization only ever added the keys, so a panel that
picked them up in an earlier pass kept them even after its type stopped being
eligible. The keys and the title suffix are now removed from ineligible panels,
which is what actually cleared the two timeline panels here.

Verified per dashboard: no panel lost, every targets block byte-identical, ids
1..N, no grid overlaps, and repeat present only on stat/gauge/bargauge.
2026-08-07 12:25:50 +01:00
Pratik Mankawde
ab08143715 Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation 2026-08-07 11:58:12 +01:00
Pratik Mankawde
0accb580f8 style(telemetry): lead phase-9 rows with single-value panels, fix two labels
Applies the guideline-8 ordering to the two dashboards introduced on this
branch, and clears the last two labelling gaps found while auditing the set:

  - peer-quality, validator-health: stats and bar gauges now lead their row
    section instead of trailing the charts, matching the other dashboards.
  - node-health "Peer Disconnects (Resources)": had an empty axisLabel, the
    only chart in the set without one. Now reads "Disconnects".
  - rpc-performance row "Aggregate RPC (all commands)" -> "(All Commands)",
    the one title that was not in Title Case.

Reordering stays inside each row section so no panel changes category, and the
panel objects are cut and re-spliced as raw text, so their contents are
byte-identical. Verified against origin/phase9: no panel lost, every targets
block unchanged, ids 1..N, and every row section now leads with its
single-value panels.
2026-08-07 11:57:09 +01:00
Pratik Mankawde
3165c0aa9c Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill
# Conflicts:
#	docker/telemetry/grafana/dashboards/consensus-health.json
#	docker/telemetry/grafana/dashboards/ledger-data-sync.json
#	docker/telemetry/grafana/dashboards/ledger-operations.json
#	docker/telemetry/grafana/dashboards/node-health.json
#	docker/telemetry/grafana/dashboards/rpc-pathfinding.json
#	docker/telemetry/grafana/dashboards/rpc-performance.json
#	docker/telemetry/grafana/dashboards/transaction-overview.json
2026-08-07 11:56:44 +01:00