tronprotocol / tronprotocol/java-tron
[Feature] Remove the legacy Monitor API and non-Prometheus metrics implementation
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 4.2k
- Forks
- 1.7k
- Avg merge
- 6d 20h
- Merged PRs (30d)
- 14
Description
Summary
java-tron maintains two parallel metrics stacks with independent switches: a legacy Dropwizard-based implementation (org.tron.core.metrics, gated by node.metricsEnable) exposed only through the gRPC MonitorApi.getStatsInfo endpoint and the HTTP /monitor/getstatsinfo servlet, and the Prometheus stack (org.tron.common.prometheus) that has become the standard monitoring path.
This proposal stages the decommissioning of the legacy stack together with MonitorApi over two releases, leaving Prometheus as the single supported monitoring backend. Phase 1 (next release) migrates the functional consumer, adds fetch-behavior counters that are retained long-term, and marks the legacy APIs deprecated while keeping them fully functional. Phase 2 (the agreed major release) removes the legacy implementation, its configuration chain, the Dropwizard dependency, and the protobuf definitions. The proposal also adds a tron:node_info{version, genesis_block_id} info metric so that node information stays easy to observe.
This is the implementation issue for item 7 of the tracking issue #6921.
Problem
Motivation
Prometheus has become the standard monitoring solution for java-tron (see #6590 and the tron-docker metric_monitor reference stack). Keeping a second, non-Prometheus implementation alongside it means duplicated instrumentation and extra maintenance, and it misleads operators: enabling node.metricsEnable without Prometheus produces no scrapeable output at all. The legacy endpoints have no known production consumers.
Current State
- The legacy stack writes into a Dropwizard
MetricRegistryand is served only viaMonitorApi.getStatsInfo(gRPC) and/monitor/getstatsinfo(HTTP). Most of its fields already have Prometheus equivalents. - The legacy registry is not purely observational: fetch-block peer selection (
FetchBlockService.getPeerTop75) consumes the per-peernet.latency.fetch.block.<peerIP>75th-percentile histogram as functional failover input. These histograms are written viahistogramUpdateUnCheck— bypassing the metrics-enable gate — use one unbounded key per peer IP that is never cleaned on disconnect, and mix latency samples from different block-fetch paths, so the choice of metrics backend silently dictates scheduling behavior. - A few node-level fields exist only in the legacy payload — notably the node version — and are not currently exposed to Prometheus.
Limitations or Risks
- The fetch-block peer-selection read must be migrated before the legacy registry can be removed.
- During Phase 1 the legacy endpoints remain functional, so the legacy instrumentation stays in place until Phase 2.
- Operators with
node.metricsEnable=truewho skip the deprecation window must not silently lose monitoring: the Phase 2 release retains a tombstone warning when the key is still present.
Proposed Solution
Proposed Design
The change is staged over two releases.
Phase 1 (next release) — migrate the functional consumer and deprecate the legacy APIs.
Fetch-block peer selection. Fetch-block peer selection switches from the per-IP P75 histogram to a bounded, per-connection estimate of actual block-fetch duration. Each PeerConnection holds an explicit unsampled state plus a volatile long EWMA. While unsampled, reads fall back to the libp2p channel RTT (Channel.getAvgLatency(), the same signal PeerManager.sortPeers already uses); the first real fetched-block measurement replaces the fallback instead of entering the EWMA, and smoothing starts from the second measurement. The steady-state weight is a fixed α = 0.1 ((ewma * 9 + last) / 10) expressed as a named constant with its rationale documented; a warm-up running mean is deliberately not used. Estimates are clamped to fetchBlockTimeout, with an explicit saturation gate that requires a strictly better candidate and an unconditional hard-timeout branch once the wall-clock fetch budget is exhausted. Because the field lives on PeerConnection, it is released with the connection — no disconnect bookkeeping and no unbounded per-IP keys. The per-IP histogram read and write sites are removed. Global fetch-latency observability stays available through the existing unlabeled Prometheus histogram (a per-peer Prometheus label would reintroduce the unbounded-cardinality problem in the TSDB).
Legacy APIs stay functional. The gRPC Monitor service and the HTTP /monitor/getstatsinfo endpoint remain registered and keep serving as before. Both are marked deprecated (option deprecated = true on service Monitor and message MetricsInfo), documented as deprecated in release notes and docs, and accompanied by a startup deprecation warning when node.metricsEnable is present plus a process-once WARN on the first invocation of either deprecated API. A legacy-field-to-Prometheus mapping table, including fields without an equivalent, is published in the PR description, release notes, and the English/Chinese documentation.
Node information. Add a tron:node_info{version, genesis_block_id} info metric (collector base name tron:node, queried as tron:node_info):
versionpreserves the node version previously carried only by the legacy payload and not currently exposed to Prometheus.genesis_block_idis the full genesis block hash — the canonical TRON chain identifier. Mainnet, Nile and private networks each have distinct values, so dashboards and PromQL can tell at a glance which network a node belongs to (e.g.tron:node_info{genesis_block_id="..."}).- Node IP is intentionally not exported; the scrape target's
instancelabel already identifies the node.
Fetch-behavior counters (retained long-term). Add three unlabeled, monotonically increasing Prometheus counters:
tron:block_fetch_armed— incremented whenFetchBlockServicearms a fetch tracking.tron:block_fetch_secondary— incremented when a secondary fetch is sent.tron:block_already_known— incremented for a matching outstanding adv request whose exact block ID is already known before processing that response. A block below the current head is not necessarily already known; it may be an unseen fork block, so the counter requires a matchingadvInvRequest.remove(item)together with the exact block ID already being known. It is a best-effort signal: concurrent arrivals may be missed, and it does not establish secondary-fetch attribution. Exact attribution remains in the experiment harness.
These counters describe fetch behavior that remains after the legacy stack is removed. They are not coupled to the Dropwizard cleanup and are retained beyond Phase 2; removing them later would be a separate observability decision. Normalized rates (secondary-fetch ratio, already-known response rate) are derived in PromQL from the counters rather than precomputed on the node.
Phase 2 (the agreed major release) — remove the legacy stack. Remove MetricsUtil, the legacy metric managers and DTOs, the gRPC MonitorApi registration, the /monitor/getstatsinfo servlet route, all legacy write sites, the node.metricsEnable configuration chain, and the Dropwizard dependency, following the WalletExtension staging precedent from #6921. Remove the protobuf definitions (service Monitor in api.proto, message MetricsInfo in Tron.proto); clients calling the removed endpoints receive UNIMPLEMENTED / 404. When node.metricsEnable is still present, emit a tombstone warning so operators who skip Phase 1 do not silently lose monitoring. The three fetch-behavior counters and tron:node_info remain. /monitor/getnodeinfo is node info, not metrics, and is unaffected.
Key Changes
- Module:
common(new info metric and fetch-behavior counters),framework(migration in Phase 1; removal in Phase 2), build files (Dropwizard dependency removed in Phase 2). - Configuration: Phase 1 keeps
node.metricsEnableworking and warns when it is present; Phase 2 removes the configuration chain and leaves a tombstone warning for a residualnode.metricsEnable.node.metrics.prometheus.enable/.portkeep their semantics throughout. - API: Phase 1 keeps gRPC
Monitor.GetStatsInfoand HTTPGET /monitor/getstatsinfofunctional but deprecated; addstron:node_info{version, genesis_block_id}and the three fetch-behavior counters. Phase 2 stops serving the legacy APIs and removes their proto definitions.
Impact
- Developer Experience: a single metrics stack to instrument and review after Phase 2; the legacy and Prometheus stacks coexist during the Phase 1 deprecation window.
- Stability: fetch-block peer selection moves from a per-tick histogram snapshot (registry lookup + locked, sorted sample copy) to an O(1) field read of a per-connection EWMA; the unbounded per-IP metric family is gone, and the scheduling signal is decoupled from the metrics backend.
- Security: the legacy gRPC/HTTP Monitor surface is removed in Phase 2; the Prometheus exporter path is untouched.
- Operations: node version and network identity remain visible in Prometheus/Grafana; the fetch-behavior counters stay available to detect regressions after the legacy stack is removed.
Compatibility
- Breaking Change: Phase 2 only. In Phase 1, gRPC
Monitor.GetStatsInfoand HTTPGET /monitor/getstatsinforemain functional; both are marked deprecated in the protos and docs with warnings. In Phase 2 they stop being served (UNIMPLEMENTED/ 404) and the protobuf definitions are removed, following principle 2 of #6921 and theWalletExtensionstaging precedent. (Today the gRPC endpoint is only registered whennode.metricsEnable=true, which defaults tofalse, and the HTTP endpoint serves mostly empty metric fields when the switch is off.) - Default Behavior Change: Yes — fetch-block peer selection now ranks peers by a decaying average (EWMA) of measured block-fetch durations, clamped to
fetchBlockTimeout, instead of the raw 75th percentile of the per-IP histogram. Semantics stay close to the old intent (actual fetch duration) while fixing its defects: mixed request sources, no decay, unbounded keys. The unsampled state falls back to the channel RTT and the first real fetch replaces it, so a newly connected peer is never ranked with a placeholder observation (previously an empty histogram silently ranked a peer as fastest — pinned by unit tests). One intentional behavior change is listed explicitly per principle 1 of #6921: previously a candidate whose P75 exceededfetchBlockTimeoutwas filtered out; with the clamped EWMA a saturated candidate stays eligible and may receive the secondary request at the hard timeout when no better candidate exists. This is a deliberate liveness improvement and is documented and tested. - Migration Required: Yes, but time-boxed — during Phase 1 operators who use the legacy stack should switch flags; the config key keeps working until Phase 2 and its presence triggers a deprecation warning, so monitoring is not lost mid-window.
| Before | After |
|---|---|
node.metricsEnable = true |
node.metrics.prometheus.enable = true |
| (legacy port n/a) | node.metrics.prometheus.port = 9527 (default) |
The key is ignored silently by config parsing in Phase 2; the release notes must call out this migration, and the tombstone warning must fire when the key is still present.
Test Plan
- New unit tests covering fetch-block peer selection and the EWMA estimator: explicit unsampled fallback, replace-on-first-sample, degradation and recovery convergence with the fixed
α = 0.1, clamp, saturation gate, hard-timeout branch, boundary values, and reconnect isolation; tests fortron:node_infoand the three fetch-behavior counters, including the precisetron:block_already_knowndefinition. - Controlled before/after experiment: warm up the estimator, degrade one peer mid-run (e.g.
tc netem), and report how many additional per-peer samples are needed for the ranking to flip; thedevelopbaseline runs the same harness and topology with the counters cherry-picked onto an instrumentation-only branch so both arms expose identical metrics. Report the normalized secondary-fetch rate and already-known response count; primary/secondary attribution comes from the harness. Measured replacement for the earlier one-or-two-sample phrasing: atfetchBlockTimeout = 200the estimate reaches its clamp within 1-2 in-window samples and the first switch lands on block 2-3 of a degradation window; atfetchBlockTimeout = 500sub-budget degradation does not saturate and only the comparison path fires (decision traces linked from the PR). - Repo CI: build matrix, checkstyle, CodeQL, single-node integration, and the changed-line coverage gate (> 60%).
Rollback
Phase 1: revert the PR commits; the protobuf definitions are unchanged (only marked deprecated), so the previous behavior is restored without protocol or config migration. Phase 2: revert reintroduces the legacy endpoints and the Dropwizard dependency, and requires restoring the removed proto definitions.
References
- Parent tracking issue: #6921 (item 7).
- #6665 (InfluxDB removal; kept the endpoint as auxiliary — this proposal deprecates and then removes it across two releases).
Additional Notes
- Do you have ideas regarding implementation? Yes — see Proposed Design (bounded per-connection fetch-duration estimator); the pull request will reference this issue.
- Are you willing to implement this feature? Yes
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with FetchBlockService.getPeerTop75 and PeerConnection to trace the current peer-selection signal, then review the common and framework modules, api.proto, Tron.proto, and the build files. The Phase 1 work is done when fetch selection uses the per-connection estimator, the node-info metric and three counters are covered by unit tests, and the legacy APIs remain functional but deprecated; Phase 2 removes the legacy stack and configuration.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend, observability-sre
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100