From 9b80f91455bc48c9f55f8564bfc46c1f26611e28 Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 25 Dec 2025 17:05:45 +0800 Subject: [PATCH 01/12] Add SR set change and empty block detection --- .../tron/common/prometheus/MetricKeys.java | 2 + .../tron/common/prometheus/MetricLabels.java | 2 + .../common/prometheus/MetricsCounter.java | 2 + .../blockchain/BlockChainMetricManager.java | 43 +++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java index 87ab6fae0a3..503d7c0a6f7 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java @@ -14,6 +14,8 @@ public static class Counter { public static final String TXS = "tron:txs"; public static final String MINER = "tron:miner"; public static final String BLOCK_FORK = "tron:block_fork"; + public static final String BLOCK_EMPTY = "tron:block_empty"; + public static final String SR_SET_CHANGE = "tron:sr_set_change"; public static final String P2P_ERROR = "tron:p2p_error"; public static final String P2P_DISCONNECT = "tron:p2p_disconnect"; public static final String INTERNAL_SERVICE_FAIL = "tron:internal_service_fail"; diff --git a/common/src/main/java/org/tron/common/prometheus/MetricLabels.java b/common/src/main/java/org/tron/common/prometheus/MetricLabels.java index 2aa3c1e3378..875e03d8110 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricLabels.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricLabels.java @@ -31,6 +31,8 @@ public static class Counter { public static final String TXS_FAIL_SIG = "sig"; public static final String TXS_FAIL_TAPOS = "tapos"; public static final String TXS_FAIL_DUP = "dup"; + public static final String SR_ADD = "add"; + public static final String SR_REMOVE = "remove"; private Counter() { throw new IllegalStateException("Counter"); diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java b/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java index 6acdf23b3bc..db25ead7a79 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java @@ -14,6 +14,8 @@ class MetricsCounter { init(MetricKeys.Counter.TXS, "tron txs info .", "type", "detail"); init(MetricKeys.Counter.MINER, "tron miner info .", "miner", "type"); init(MetricKeys.Counter.BLOCK_FORK, "tron block fork info .", "type"); + init(MetricKeys.Counter.BLOCK_EMPTY, "tron empty block count .", "miner"); + init(MetricKeys.Counter.SR_SET_CHANGE, "tron sr set change .", "action", "witness"); init(MetricKeys.Counter.P2P_ERROR, "tron p2p error info .", "type"); init(MetricKeys.Counter.P2P_DISCONNECT, "tron p2p disconnect .", "type"); init(MetricKeys.Counter.INTERNAL_SERVICE_FAIL, "internal Service fail.", diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index 384f1d8add1..f1f87ab8e23 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -3,10 +3,13 @@ import com.codahale.metrics.Counter; import com.google.protobuf.ByteString; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import lombok.Getter; import lombok.Setter; import org.bouncycastle.util.encoders.Hex; @@ -42,6 +45,7 @@ public class BlockChainMetricManager { private long failProcessBlockNum = 0; @Setter private String failProcessBlockReason = ""; + private final Set lastActiveWitnesses = ConcurrentHashMap.newKeySet(); public BlockChainInfo getBlockChainInfo() { BlockChainInfo blockChainInfo = new BlockChainInfo(); @@ -168,6 +172,45 @@ public void applyBlock(BlockCapsule block) { MetricsUtil.meterMark(MetricsKey.BLOCKCHAIN_TPS, block.getTransactions().size()); Metrics.counterInc(MetricKeys.Counter.TXS, block.getTransactions().size(), MetricLabels.Counter.TXS_SUCCESS, MetricLabels.Counter.TXS_SUCCESS); + } else { + // Empty block + Metrics.counterInc(MetricKeys.Counter.BLOCK_EMPTY, 1, + StringUtil.encode58Check(address)); + } + + // SR set change detection + Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() + .stream() + .map(w -> Hex.toHexString(w.toByteArray())) + .collect(Collectors.toSet()); + recordSrSetChange(currentWitnesses); + } + + private void recordSrSetChange(Set currentWitnesses) { + if (currentWitnesses.isEmpty()) { + return; + } + if (lastActiveWitnesses.isEmpty()) { + lastActiveWitnesses.addAll(currentWitnesses); + return; + } + Set added = new HashSet<>(currentWitnesses); + added.removeAll(lastActiveWitnesses); + + Set removed = new HashSet<>(lastActiveWitnesses); + removed.removeAll(currentWitnesses); + + for (String address : added) { + Metrics.counterInc(MetricKeys.Counter.SR_SET_CHANGE, 1, + MetricLabels.Counter.SR_ADD, StringUtil.encode58Check(Hex.decode(address))); + } + for (String address : removed) { + Metrics.counterInc(MetricKeys.Counter.SR_SET_CHANGE, 1, + MetricLabels.Counter.SR_REMOVE, StringUtil.encode58Check(Hex.decode(address))); + } + if (!added.isEmpty() || !removed.isEmpty()) { + lastActiveWitnesses.clear(); + lastActiveWitnesses.addAll(currentWitnesses); } } From 1d2de1a1598117d6618361bec3b0a9d7324672e9 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 9 Jan 2026 17:35:16 +0800 Subject: [PATCH 02/12] Add nextMaintenanceTime judge --- .../blockchain/BlockChainMetricManager.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index f1f87ab8e23..f569767ef40 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -46,6 +46,7 @@ public class BlockChainMetricManager { @Setter private String failProcessBlockReason = ""; private final Set lastActiveWitnesses = ConcurrentHashMap.newKeySet(); + private long lastNextMaintenanceTime = 0; public BlockChainInfo getBlockChainInfo() { BlockChainInfo blockChainInfo = new BlockChainInfo(); @@ -179,11 +180,19 @@ public void applyBlock(BlockCapsule block) { } // SR set change detection - Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() - .stream() - .map(w -> Hex.toHexString(w.toByteArray())) - .collect(Collectors.toSet()); - recordSrSetChange(currentWitnesses); + long nextMaintenanceTime = dbManager.getDynamicPropertiesStore().getNextMaintenanceTime(); + if (lastNextMaintenanceTime == 0) { + lastNextMaintenanceTime = nextMaintenanceTime; + lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() + .stream().map(w -> Hex.toHexString(w.toByteArray())).collect(Collectors.toSet())); + } else if (nextMaintenanceTime != lastNextMaintenanceTime) { + Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() + .stream() + .map(w -> Hex.toHexString(w.toByteArray())) + .collect(Collectors.toSet()); + recordSrSetChange(currentWitnesses); + lastNextMaintenanceTime = nextMaintenanceTime; + } } private void recordSrSetChange(Set currentWitnesses) { From 1c0c1b789713ff721bd975c11271fc69abbf14a7 Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 16 Jan 2026 11:47:03 +0800 Subject: [PATCH 03/12] Empty Block check unit test done --- .../prometheus/PrometheusApiServiceTest.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index d4d758b7a98..ca95d03281b 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -25,6 +25,7 @@ import org.tron.common.utils.ByteArray; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.Sha256Hash; +import org.tron.common.utils.StringUtil; import org.tron.common.utils.Utils; import org.tron.consensus.dpos.DposSlot; import org.tron.core.ChainBaseManager; @@ -65,7 +66,7 @@ protected static void initParameter(CommonParameter parameter) { parameter.setMetricsPrometheusEnable(true); } - protected void check() throws Exception { + protected void check(byte[] address) throws Exception { Double memoryBytes = CollectorRegistry.defaultRegistry.getSampleValue( "system_total_physical_memory_bytes"); Assert.assertNotNull(memoryBytes); @@ -80,6 +81,17 @@ protected void check() throws Exception { new String[] {"sync"}, new String[] {"false"}); Assert.assertNotNull(pushBlock); Assert.assertEquals(pushBlock.intValue(), blocks + 1); + + String minerBase58 = StringUtil.encode58Check(address); + Double emptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:block_empty_total", new String[] {"miner"}, new String[] {minerBase58}); + + Assert.assertNotNull(emptyBlock); + // The initial address is in the active witness list along with 2 randomly generated witnesses, + // so it produces blocks every 3 slots. Total empty blocks = 1 (first manual block) + blocks/3 + // (from the loop) + 1 if blocks%3 != 0 (partial round) + Assert.assertEquals(emptyBlock.intValue(), 1 + blocks / 3 + (blocks % 3 != 0 ? 1 : 0)); + Double errorLogs = CollectorRegistry.defaultRegistry.getSampleValue( "tron:error_info_total", new String[] {"net"}, new String[] {MetricLabels.UNDEFINED}); Assert.assertNull(errorLogs); @@ -133,7 +145,7 @@ public void testMetric() throws Exception { for (int i = 0; i < blocks; i++) { generateBlock(witnessAndAccount); } - check(); + check(address); } private Map addTestWitnessAndAccount() { From bc87e2e555533de5b8472ebe3feb53c322ba9e6e Mon Sep 17 00:00:00 2001 From: warku123 Date: Fri, 16 Jan 2026 16:35:20 +0800 Subject: [PATCH 04/12] SR change unit test --- .../blockchain/BlockChainMetricManager.java | 12 +--- .../prometheus/PrometheusApiServiceTest.java | 55 +++++++++++++++++-- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index f569767ef40..7ef9e8f59e8 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -46,7 +46,8 @@ public class BlockChainMetricManager { @Setter private String failProcessBlockReason = ""; private final Set lastActiveWitnesses = ConcurrentHashMap.newKeySet(); - private long lastNextMaintenanceTime = 0; + // To control SR set change metric update logic, -1 means not initialized + private long lastNextMaintenanceTime = -1; public BlockChainInfo getBlockChainInfo() { BlockChainInfo blockChainInfo = new BlockChainInfo(); @@ -181,7 +182,7 @@ public void applyBlock(BlockCapsule block) { // SR set change detection long nextMaintenanceTime = dbManager.getDynamicPropertiesStore().getNextMaintenanceTime(); - if (lastNextMaintenanceTime == 0) { + if (lastNextMaintenanceTime == -1) { lastNextMaintenanceTime = nextMaintenanceTime; lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() .stream().map(w -> Hex.toHexString(w.toByteArray())).collect(Collectors.toSet())); @@ -196,13 +197,6 @@ public void applyBlock(BlockCapsule block) { } private void recordSrSetChange(Set currentWitnesses) { - if (currentWitnesses.isEmpty()) { - return; - } - if (lastActiveWitnesses.isEmpty()) { - lastActiveWitnesses.addAll(currentWitnesses); - return; - } Set added = new HashSet<>(currentWitnesses); added.removeAll(lastActiveWitnesses); diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index ca95d03281b..38cc171f152 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -7,6 +7,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -66,7 +67,7 @@ protected static void initParameter(CommonParameter parameter) { parameter.setMetricsPrometheusEnable(true); } - protected void check(byte[] address) throws Exception { + protected void check(byte[] address, Map witnessAndAccount) throws Exception { Double memoryBytes = CollectorRegistry.defaultRegistry.getSampleValue( "system_total_physical_memory_bytes"); Assert.assertNotNull(memoryBytes); @@ -87,10 +88,42 @@ protected void check(byte[] address) throws Exception { "tron:block_empty_total", new String[] {"miner"}, new String[] {minerBase58}); Assert.assertNotNull(emptyBlock); - // The initial address is in the active witness list along with 2 randomly generated witnesses, - // so it produces blocks every 3 slots. Total empty blocks = 1 (first manual block) + blocks/3 - // (from the loop) + 1 if blocks%3 != 0 (partial round) - Assert.assertEquals(emptyBlock.intValue(), 1 + blocks / 3 + (blocks % 3 != 0 ? 1 : 0)); + Assert.assertEquals(emptyBlock.intValue(), 1); + + // Check SR_REMOVE for initial address (removed when addTestWitnessAndAccount() is called) + Double srRemoveCount = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:sr_set_change_total", + new String[] {"action", "witness"}, + new String[] {MetricLabels.Counter.SR_REMOVE, minerBase58} + ); + Assert.assertNotNull(srRemoveCount); + Assert.assertEquals(1, srRemoveCount.intValue()); + + // Check SR_ADD and empty blocks for each new witness in witnessAndAccount (excluding initial address) + ByteString addressByteString = ByteString.copyFrom(address); + double totalNewWitnessEmptyBlocks = 0; + for (ByteString witnessAddress : witnessAndAccount.keySet()) { + if (witnessAddress.equals(addressByteString)) { + continue; // Skip initial address + } + String witnessBase58 = StringUtil.encode58Check(witnessAddress.toByteArray()); + + // Check SR_ADD + Double srAddCount = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:sr_set_change_total", + new String[] {"action", "witness"}, + new String[] {MetricLabels.Counter.SR_ADD, witnessBase58} + ); + Assert.assertNotNull("SR_ADD should be recorded for witness: " + witnessBase58, srAddCount); + Assert.assertEquals("Each new witness should have 1 SR_ADD record", 1, srAddCount.intValue()); + + // Collect empty blocks count + Double witnessEmptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:block_empty_total", new String[] {"miner"}, new String[] {witnessBase58}); + Assert.assertNotNull(witnessEmptyBlock); + totalNewWitnessEmptyBlocks += witnessEmptyBlock; + } + Assert.assertEquals(blocks, (int)totalNewWitnessEmptyBlocks); Double errorLogs = CollectorRegistry.defaultRegistry.getSampleValue( "tron:error_info_total", new String[] {"net"}, new String[] {MetricLabels.UNDEFINED}); @@ -142,10 +175,20 @@ public void testMetric() throws Exception { Map witnessAndAccount = addTestWitnessAndAccount(); witnessAndAccount.put(ByteString.copyFrom(address), key); + + // Explicitly update WitnessScheduleStore to remove initial address, triggering SR_REMOVE metric + List newActiveWitnesses = new ArrayList<>(witnessAndAccount.keySet()); + newActiveWitnesses.remove(ByteString.copyFrom(address)); + chainBaseManager.getWitnessScheduleStore().saveActiveWitnesses(newActiveWitnesses); + + // Update nextMaintenanceTime to trigger SR set change detection + long nextMaintenanceTime = chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime(); + chainBaseManager.getDynamicPropertiesStore().updateNextMaintenanceTime(nextMaintenanceTime + 3600_000L); + for (int i = 0; i < blocks; i++) { generateBlock(witnessAndAccount); } - check(address); + check(address, witnessAndAccount); } private Map addTestWitnessAndAccount() { From 6bece439b14efecd767fffa137e1801233d5032d Mon Sep 17 00:00:00 2001 From: warku123 Date: Tue, 24 Mar 2026 11:50:52 +0800 Subject: [PATCH 05/12] feat(metrics): add block transaction count histogram for empty block monitoring Replace the dedicated tron:block_empty_total counter with a more comprehensive tron:block_transaction_count histogram that tracks the distribution of transaction counts per block. Changes: - Add overloaded init() method in MetricsHistogram to support custom buckets - Add BLOCK_TRANSACTION_COUNT histogram with buckets [0, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000] - Record transaction count for all blocks (including empty blocks with txCount=0) - Empty blocks can be queried via bucket le=0.0 - Remove unused BLOCK_EMPTY counter This provides richer insights for network analysis while still supporting empty block monitoring via histogram bucket queries. Closes #6590 --- .../tron/common/prometheus/MetricKeys.java | 2 +- .../common/prometheus/MetricsCounter.java | 1 - .../common/prometheus/MetricsHistogram.java | 16 +++++++++ .../blockchain/BlockChainMetricManager.java | 8 ++--- .../prometheus/PrometheusApiServiceTest.java | 36 ++++++++++++------- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java index 503d7c0a6f7..3293a67342a 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricKeys.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricKeys.java @@ -14,7 +14,6 @@ public static class Counter { public static final String TXS = "tron:txs"; public static final String MINER = "tron:miner"; public static final String BLOCK_FORK = "tron:block_fork"; - public static final String BLOCK_EMPTY = "tron:block_empty"; public static final String SR_SET_CHANGE = "tron:sr_set_change"; public static final String P2P_ERROR = "tron:p2p_error"; public static final String P2P_DISCONNECT = "tron:p2p_disconnect"; @@ -64,6 +63,7 @@ public static class Histogram { public static final String MESSAGE_PROCESS_LATENCY = "tron:message_process_latency_seconds"; public static final String BLOCK_FETCH_LATENCY = "tron:block_fetch_latency_seconds"; public static final String BLOCK_RECEIVE_DELAY = "tron:block_receive_delay_seconds"; + public static final String BLOCK_TRANSACTION_COUNT = "tron:block_transaction_count"; private Histogram() { throw new IllegalStateException("Histogram"); diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java b/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java index db25ead7a79..7231baaba8f 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsCounter.java @@ -14,7 +14,6 @@ class MetricsCounter { init(MetricKeys.Counter.TXS, "tron txs info .", "type", "detail"); init(MetricKeys.Counter.MINER, "tron miner info .", "miner", "type"); init(MetricKeys.Counter.BLOCK_FORK, "tron block fork info .", "type"); - init(MetricKeys.Counter.BLOCK_EMPTY, "tron empty block count .", "miner"); init(MetricKeys.Counter.SR_SET_CHANGE, "tron sr set change .", "action", "witness"); init(MetricKeys.Counter.P2P_ERROR, "tron p2p error info .", "type"); init(MetricKeys.Counter.P2P_DISCONNECT, "tron p2p disconnect .", "type"); diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java index 556db10feb5..9042f17f3f5 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java @@ -48,6 +48,11 @@ public class MetricsHistogram { init(MetricKeys.Histogram.BLOCK_FETCH_LATENCY, "fetch block latency."); init(MetricKeys.Histogram.BLOCK_RECEIVE_DELAY, "receive block delay time, receiveTime - blockTime."); + + init(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, + "Distribution of transaction counts per block.", + new double[]{0, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000}, + "miner"); } private MetricsHistogram() { @@ -62,6 +67,17 @@ private static void init(String name, String help, String... labels) { .register()); } + private static void init(String name, String help, double[] buckets, String... labels) { + Histogram.Builder builder = Histogram.build() + .name(name) + .help(help) + .labelNames(labels); + if (buckets != null && buckets.length > 0) { + builder.buckets(buckets); + } + container.put(name, builder.register()); + } + static Histogram.Timer startTimer(String key, String... labels) { if (Metrics.enabled()) { Histogram histogram = container.get(key); diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index 7ef9e8f59e8..882114ba2a9 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -174,11 +174,11 @@ public void applyBlock(BlockCapsule block) { MetricsUtil.meterMark(MetricsKey.BLOCKCHAIN_TPS, block.getTransactions().size()); Metrics.counterInc(MetricKeys.Counter.TXS, block.getTransactions().size(), MetricLabels.Counter.TXS_SUCCESS, MetricLabels.Counter.TXS_SUCCESS); - } else { - // Empty block - Metrics.counterInc(MetricKeys.Counter.BLOCK_EMPTY, 1, - StringUtil.encode58Check(address)); } + // Record transaction count distribution for all blocks (including empty blocks) + int txCount = block.getTransactions().size(); + Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, txCount, + StringUtil.encode58Check(address)); // SR set change detection long nextMaintenanceTime = dbManager.getDynamicPropertiesStore().getNextMaintenanceTime(); diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index 38cc171f152..eb5f53be6d0 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -84,11 +84,13 @@ protected void check(byte[] address, Map witnessAndAccount) Assert.assertEquals(pushBlock.intValue(), blocks + 1); String minerBase58 = StringUtil.encode58Check(address); + // Query histogram bucket le="0.0" for empty blocks Double emptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( - "tron:block_empty_total", new String[] {"miner"}, new String[] {minerBase58}); - - Assert.assertNotNull(emptyBlock); - Assert.assertEquals(emptyBlock.intValue(), 1); + "tron:block_transaction_count_bucket", + new String[] {"miner", "le"}, new String[] {minerBase58, "0.0"}); + + Assert.assertNotNull("Empty block bucket should exist for miner: " + minerBase58, emptyBlock); + Assert.assertEquals("Should have 1 empty block", 1, emptyBlock.intValue()); // Check SR_REMOVE for initial address (removed when addTestWitnessAndAccount() is called) Double srRemoveCount = CollectorRegistry.defaultRegistry.getSampleValue( @@ -99,7 +101,8 @@ protected void check(byte[] address, Map witnessAndAccount) Assert.assertNotNull(srRemoveCount); Assert.assertEquals(1, srRemoveCount.intValue()); - // Check SR_ADD and empty blocks for each new witness in witnessAndAccount (excluding initial address) + // Check SR_ADD and empty blocks for each new witness in witnessAndAccount + // (excluding initial address) ByteString addressByteString = ByteString.copyFrom(address); double totalNewWitnessEmptyBlocks = 0; for (ByteString witnessAddress : witnessAndAccount.keySet()) { @@ -114,13 +117,17 @@ protected void check(byte[] address, Map witnessAndAccount) new String[] {"action", "witness"}, new String[] {MetricLabels.Counter.SR_ADD, witnessBase58} ); - Assert.assertNotNull("SR_ADD should be recorded for witness: " + witnessBase58, srAddCount); - Assert.assertEquals("Each new witness should have 1 SR_ADD record", 1, srAddCount.intValue()); + Assert.assertNotNull("SR_ADD should be recorded for witness: " + witnessBase58, + srAddCount); + Assert.assertEquals("Each new witness should have 1 SR_ADD record", 1, + srAddCount.intValue()); - // Collect empty blocks count + // Collect empty blocks count from histogram bucket Double witnessEmptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( - "tron:block_empty_total", new String[] {"miner"}, new String[] {witnessBase58}); - Assert.assertNotNull(witnessEmptyBlock); + "tron:block_transaction_count_bucket", + new String[] {"miner", "le"}, new String[] {witnessBase58, "0.0"}); + Assert.assertNotNull("Empty block bucket should exist for witness: " + witnessBase58, + witnessEmptyBlock); totalNewWitnessEmptyBlocks += witnessEmptyBlock; } Assert.assertEquals(blocks, (int)totalNewWitnessEmptyBlocks); @@ -176,14 +183,17 @@ public void testMetric() throws Exception { Map witnessAndAccount = addTestWitnessAndAccount(); witnessAndAccount.put(ByteString.copyFrom(address), key); - // Explicitly update WitnessScheduleStore to remove initial address, triggering SR_REMOVE metric + // Explicitly update WitnessScheduleStore to remove initial address, + // triggering SR_REMOVE metric List newActiveWitnesses = new ArrayList<>(witnessAndAccount.keySet()); newActiveWitnesses.remove(ByteString.copyFrom(address)); chainBaseManager.getWitnessScheduleStore().saveActiveWitnesses(newActiveWitnesses); // Update nextMaintenanceTime to trigger SR set change detection - long nextMaintenanceTime = chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime(); - chainBaseManager.getDynamicPropertiesStore().updateNextMaintenanceTime(nextMaintenanceTime + 3600_000L); + long nextMaintenanceTime = + chainBaseManager.getDynamicPropertiesStore().getNextMaintenanceTime(); + chainBaseManager.getDynamicPropertiesStore().updateNextMaintenanceTime( + nextMaintenanceTime + 3600_000L); for (int i = 0; i < blocks; i++) { generateBlock(witnessAndAccount); From 39dd708d6b18fc335cbb2e27d3cecfff5fe3c0dc Mon Sep 17 00:00:00 2001 From: warku123 Date: Wed, 1 Apr 2026 20:45:26 +0800 Subject: [PATCH 06/12] Modify blank to passed the ci check --- .../tron/core/metrics/blockchain/BlockChainMetricManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index 882114ba2a9..a57bfa8f073 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -47,7 +47,7 @@ public class BlockChainMetricManager { private String failProcessBlockReason = ""; private final Set lastActiveWitnesses = ConcurrentHashMap.newKeySet(); // To control SR set change metric update logic, -1 means not initialized - private long lastNextMaintenanceTime = -1; + private long lastNextMaintenanceTime = -1; public BlockChainInfo getBlockChainInfo() { BlockChainInfo blockChainInfo = new BlockChainInfo(); From e92b8e2e72e22347f33d01280352bd2466a3ce22 Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 2 Apr 2026 11:20:24 +0800 Subject: [PATCH 07/12] refactor(metrics): extract MINER_LABEL constant for histogram labels Extract repeated 'miner' string literal into MINER_LABEL constant in MetricsHistogram to follow DRY principle and fix SonarQube warning. Also update PrometheusApiServiceTest to use MINER_LABEL constant for test assertions. Relates #6624 --- .../java/org/tron/common/prometheus/MetricsHistogram.java | 7 ++++--- .../core/metrics/prometheus/PrometheusApiServiceTest.java | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java index 9042f17f3f5..29363e1e428 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java @@ -9,6 +9,7 @@ public class MetricsHistogram { private static final Map container = new ConcurrentHashMap<>(); + private static final String MINER_LABEL = "miner"; static { init(MetricKeys.Histogram.INTERNAL_SERVICE_LATENCY, "Internal Service latency.", @@ -20,7 +21,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.JSONRPC_SERVICE_LATENCY, "JsonRpc Service latency.", "method"); init(MetricKeys.Histogram.MINER_LATENCY, "miner latency.", - "miner"); + MINER_LABEL); init(MetricKeys.Histogram.PING_PONG_LATENCY, "node ping pong latency."); init(MetricKeys.Histogram.VERIFY_SIGN_LATENCY, "verify sign latency for trx , block.", "type"); @@ -36,7 +37,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.PROCESS_TRANSACTION_LATENCY, "process transaction latency.", "type", "contract"); init(MetricKeys.Histogram.MINER_DELAY, "miner delay time, actualTime - planTime.", - "miner"); + MINER_LABEL); init(MetricKeys.Histogram.UDP_BYTES, "udp_bytes traffic.", "type"); init(MetricKeys.Histogram.TCP_BYTES, "tcp_bytes traffic.", @@ -52,7 +53,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, "Distribution of transaction counts per block.", new double[]{0, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000}, - "miner"); + MINER_LABEL); } private MetricsHistogram() { diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index eb5f53be6d0..3d19654e820 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -40,6 +40,9 @@ @Slf4j(topic = "metric") public class PrometheusApiServiceTest extends BaseTest { + + private static final String MINER_LABEL = "miner"; + static LocalDateTime localDateTime = LocalDateTime.now(); @Resource private DposSlot dposSlot; @@ -87,7 +90,7 @@ protected void check(byte[] address, Map witnessAndAccount) // Query histogram bucket le="0.0" for empty blocks Double emptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( "tron:block_transaction_count_bucket", - new String[] {"miner", "le"}, new String[] {minerBase58, "0.0"}); + new String[] {MINER_LABEL, "le"}, new String[] {minerBase58, "0.0"}); Assert.assertNotNull("Empty block bucket should exist for miner: " + minerBase58, emptyBlock); Assert.assertEquals("Should have 1 empty block", 1, emptyBlock.intValue()); @@ -125,7 +128,7 @@ protected void check(byte[] address, Map witnessAndAccount) // Collect empty blocks count from histogram bucket Double witnessEmptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( "tron:block_transaction_count_bucket", - new String[] {"miner", "le"}, new String[] {witnessBase58, "0.0"}); + new String[] {MINER_LABEL, "le"}, new String[] {witnessBase58, "0.0"}); Assert.assertNotNull("Empty block bucket should exist for witness: " + witnessBase58, witnessEmptyBlock); totalNewWitnessEmptyBlocks += witnessEmptyBlock; From 846e3d3088f2914e2273b5de79dab6a5350cebf7 Mon Sep 17 00:00:00 2001 From: warku123 Date: Wed, 8 Apr 2026 16:40:50 +0800 Subject: [PATCH 08/12] refactor(metrics): address PR review feedback - Extract MINER_LABEL to MetricLabels.Histogram.MINER as shared constant - Use int instead of double for block count in tests - Store witness addresses as base58 directly, removing unnecessary hex round-trip Co-Authored-By: Claude Opus 4.6 --- .../org/tron/common/prometheus/MetricLabels.java | 1 + .../tron/common/prometheus/MetricsHistogram.java | 7 +++---- .../blockchain/BlockChainMetricManager.java | 8 ++++---- .../prometheus/PrometheusApiServiceTest.java | 14 ++++++-------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/common/src/main/java/org/tron/common/prometheus/MetricLabels.java b/common/src/main/java/org/tron/common/prometheus/MetricLabels.java index 875e03d8110..1f0da214085 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricLabels.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricLabels.java @@ -68,6 +68,7 @@ private Gauge() { // Histogram public static class Histogram { + public static final String MINER = "miner"; public static final String TRAFFIC_IN = "in"; public static final String TRAFFIC_OUT = "out"; diff --git a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java index 29363e1e428..6a66dc76bb3 100644 --- a/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java +++ b/common/src/main/java/org/tron/common/prometheus/MetricsHistogram.java @@ -9,7 +9,6 @@ public class MetricsHistogram { private static final Map container = new ConcurrentHashMap<>(); - private static final String MINER_LABEL = "miner"; static { init(MetricKeys.Histogram.INTERNAL_SERVICE_LATENCY, "Internal Service latency.", @@ -21,7 +20,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.JSONRPC_SERVICE_LATENCY, "JsonRpc Service latency.", "method"); init(MetricKeys.Histogram.MINER_LATENCY, "miner latency.", - MINER_LABEL); + MetricLabels.Histogram.MINER); init(MetricKeys.Histogram.PING_PONG_LATENCY, "node ping pong latency."); init(MetricKeys.Histogram.VERIFY_SIGN_LATENCY, "verify sign latency for trx , block.", "type"); @@ -37,7 +36,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.PROCESS_TRANSACTION_LATENCY, "process transaction latency.", "type", "contract"); init(MetricKeys.Histogram.MINER_DELAY, "miner delay time, actualTime - planTime.", - MINER_LABEL); + MetricLabels.Histogram.MINER); init(MetricKeys.Histogram.UDP_BYTES, "udp_bytes traffic.", "type"); init(MetricKeys.Histogram.TCP_BYTES, "tcp_bytes traffic.", @@ -53,7 +52,7 @@ public class MetricsHistogram { init(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, "Distribution of transaction counts per block.", new double[]{0, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000}, - MINER_LABEL); + MetricLabels.Histogram.MINER); } private MetricsHistogram() { diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index a57bfa8f073..a2de81fb4c9 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -185,11 +185,11 @@ public void applyBlock(BlockCapsule block) { if (lastNextMaintenanceTime == -1) { lastNextMaintenanceTime = nextMaintenanceTime; lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() - .stream().map(w -> Hex.toHexString(w.toByteArray())).collect(Collectors.toSet())); + .stream().map(w -> StringUtil.encode58Check(w.toByteArray())).collect(Collectors.toSet())); } else if (nextMaintenanceTime != lastNextMaintenanceTime) { Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() .stream() - .map(w -> Hex.toHexString(w.toByteArray())) + .map(w -> StringUtil.encode58Check(w.toByteArray())) .collect(Collectors.toSet()); recordSrSetChange(currentWitnesses); lastNextMaintenanceTime = nextMaintenanceTime; @@ -205,11 +205,11 @@ private void recordSrSetChange(Set currentWitnesses) { for (String address : added) { Metrics.counterInc(MetricKeys.Counter.SR_SET_CHANGE, 1, - MetricLabels.Counter.SR_ADD, StringUtil.encode58Check(Hex.decode(address))); + MetricLabels.Counter.SR_ADD, address); } for (String address : removed) { Metrics.counterInc(MetricKeys.Counter.SR_SET_CHANGE, 1, - MetricLabels.Counter.SR_REMOVE, StringUtil.encode58Check(Hex.decode(address))); + MetricLabels.Counter.SR_REMOVE, address); } if (!added.isEmpty() || !removed.isEmpty()) { lastActiveWitnesses.clear(); diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index 3d19654e820..ee42af4fa99 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -41,7 +41,6 @@ @Slf4j(topic = "metric") public class PrometheusApiServiceTest extends BaseTest { - private static final String MINER_LABEL = "miner"; static LocalDateTime localDateTime = LocalDateTime.now(); @Resource @@ -90,7 +89,7 @@ protected void check(byte[] address, Map witnessAndAccount) // Query histogram bucket le="0.0" for empty blocks Double emptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( "tron:block_transaction_count_bucket", - new String[] {MINER_LABEL, "le"}, new String[] {minerBase58, "0.0"}); + new String[] {MetricLabels.Histogram.MINER, "le"}, new String[] {minerBase58, "0.0"}); Assert.assertNotNull("Empty block bucket should exist for miner: " + minerBase58, emptyBlock); Assert.assertEquals("Should have 1 empty block", 1, emptyBlock.intValue()); @@ -107,7 +106,7 @@ protected void check(byte[] address, Map witnessAndAccount) // Check SR_ADD and empty blocks for each new witness in witnessAndAccount // (excluding initial address) ByteString addressByteString = ByteString.copyFrom(address); - double totalNewWitnessEmptyBlocks = 0; + int totalNewWitnessEmptyBlocks = 0; for (ByteString witnessAddress : witnessAndAccount.keySet()) { if (witnessAddress.equals(addressByteString)) { continue; // Skip initial address @@ -126,14 +125,13 @@ protected void check(byte[] address, Map witnessAndAccount) srAddCount.intValue()); // Collect empty blocks count from histogram bucket - Double witnessEmptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( + int witnessEmptyBlock = CollectorRegistry.defaultRegistry.getSampleValue( "tron:block_transaction_count_bucket", - new String[] {MINER_LABEL, "le"}, new String[] {witnessBase58, "0.0"}); - Assert.assertNotNull("Empty block bucket should exist for witness: " + witnessBase58, - witnessEmptyBlock); + new String[] {MetricLabels.Histogram.MINER, "le"}, new String[] {witnessBase58, "0.0"}) + .intValue(); totalNewWitnessEmptyBlocks += witnessEmptyBlock; } - Assert.assertEquals(blocks, (int)totalNewWitnessEmptyBlocks); + Assert.assertEquals(blocks, totalNewWitnessEmptyBlocks); Double errorLogs = CollectorRegistry.defaultRegistry.getSampleValue( "tron:error_info_total", new String[] {"net"}, new String[] {MetricLabels.UNDEFINED}); From ff17a1961b971de1149a89b9323d17703f73ef2a Mon Sep 17 00:00:00 2001 From: warku123 Date: Wed, 8 Apr 2026 16:47:47 +0800 Subject: [PATCH 09/12] fix(metrics): wrap long line to pass checkstyle 100-char limit Co-Authored-By: Claude Opus 4.6 --- .../tron/core/metrics/blockchain/BlockChainMetricManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index a2de81fb4c9..3f96d3a7903 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -185,7 +185,8 @@ public void applyBlock(BlockCapsule block) { if (lastNextMaintenanceTime == -1) { lastNextMaintenanceTime = nextMaintenanceTime; lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() - .stream().map(w -> StringUtil.encode58Check(w.toByteArray())).collect(Collectors.toSet())); + .stream().map(w -> StringUtil.encode58Check(w.toByteArray())) + .collect(Collectors.toSet())); } else if (nextMaintenanceTime != lastNextMaintenanceTime) { Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() .stream() From 7f351bf82856e69f107a4d8d791187ae1ff050f4 Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 9 Apr 2026 14:21:09 +0800 Subject: [PATCH 10/12] refactor(metrics): reuse txCount variable to avoid repeated calls Co-Authored-By: Claude Opus 4.6 --- .../core/metrics/blockchain/BlockChainMetricManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index 3f96d3a7903..45f2f67e550 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -170,13 +170,13 @@ public void applyBlock(BlockCapsule block) { } //TPS - if (block.getTransactions().size() > 0) { - MetricsUtil.meterMark(MetricsKey.BLOCKCHAIN_TPS, block.getTransactions().size()); - Metrics.counterInc(MetricKeys.Counter.TXS, block.getTransactions().size(), + int txCount = block.getTransactions().size(); + if (txCount > 0) { + MetricsUtil.meterMark(MetricsKey.BLOCKCHAIN_TPS, txCount); + Metrics.counterInc(MetricKeys.Counter.TXS, txCount, MetricLabels.Counter.TXS_SUCCESS, MetricLabels.Counter.TXS_SUCCESS); } // Record transaction count distribution for all blocks (including empty blocks) - int txCount = block.getTransactions().size(); Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, txCount, StringUtil.encode58Check(address)); From 7fdb3c59f823a9faa21cdbeefc81b0139a5c5071 Mon Sep 17 00:00:00 2001 From: warku123 Date: Tue, 14 Apr 2026 11:37:21 +0800 Subject: [PATCH 11/12] fix(metrics): use HashSet and add fork rollback guard for SR set change - Replace ConcurrentHashMap.newKeySet() with HashSet since applyBlock() is only called from a single-threaded path (synchronized on blockLock) - Add fork rollback detection: when nextMaintenanceTime < lastNextMaintenanceTime, reinitialize in-memory state instead of diffing, preventing phantom SR changes Co-Authored-By: Claude Opus 4.6 --- .../metrics/blockchain/BlockChainMetricManager.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index 45f2f67e550..a6d3fde0dea 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -45,7 +45,8 @@ public class BlockChainMetricManager { private long failProcessBlockNum = 0; @Setter private String failProcessBlockReason = ""; - private final Set lastActiveWitnesses = ConcurrentHashMap.newKeySet(); + // Only accessed from block processing thread (synchronized on blockLock) + private final Set lastActiveWitnesses = new HashSet<>(); // To control SR set change metric update logic, -1 means not initialized private long lastNextMaintenanceTime = -1; @@ -192,7 +193,13 @@ public void applyBlock(BlockCapsule block) { .stream() .map(w -> StringUtil.encode58Check(w.toByteArray())) .collect(Collectors.toSet()); - recordSrSetChange(currentWitnesses); + if (nextMaintenanceTime < lastNextMaintenanceTime) { + // Fork rollback detected — reinitialize instead of diffing + lastActiveWitnesses.clear(); + lastActiveWitnesses.addAll(currentWitnesses); + } else { + recordSrSetChange(currentWitnesses); + } lastNextMaintenanceTime = nextMaintenanceTime; } } From 779194485f646e349bd2be943f85ad6105943fe0 Mon Sep 17 00:00:00 2001 From: warku123 Date: Thu, 16 Apr 2026 12:08:59 +0800 Subject: [PATCH 12/12] perf(metrics): skip metrics computation when prometheus is disabled Wrap histogram observation and SR set change detection with Metrics.enabled() guard to avoid unnecessary computation (DB reads, encode58Check, stream mapping, HashSet diff) on every block when metrics are not enabled. --- .../blockchain/BlockChainMetricManager.java | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java index a6d3fde0dea..86c50ebec0c 100644 --- a/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java +++ b/framework/src/main/java/org/tron/core/metrics/blockchain/BlockChainMetricManager.java @@ -177,30 +177,33 @@ public void applyBlock(BlockCapsule block) { Metrics.counterInc(MetricKeys.Counter.TXS, txCount, MetricLabels.Counter.TXS_SUCCESS, MetricLabels.Counter.TXS_SUCCESS); } - // Record transaction count distribution for all blocks (including empty blocks) - Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, txCount, - StringUtil.encode58Check(address)); - - // SR set change detection - long nextMaintenanceTime = dbManager.getDynamicPropertiesStore().getNextMaintenanceTime(); - if (lastNextMaintenanceTime == -1) { - lastNextMaintenanceTime = nextMaintenanceTime; - lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() - .stream().map(w -> StringUtil.encode58Check(w.toByteArray())) - .collect(Collectors.toSet())); - } else if (nextMaintenanceTime != lastNextMaintenanceTime) { - Set currentWitnesses = chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() - .stream() - .map(w -> StringUtil.encode58Check(w.toByteArray())) - .collect(Collectors.toSet()); - if (nextMaintenanceTime < lastNextMaintenanceTime) { - // Fork rollback detected — reinitialize instead of diffing - lastActiveWitnesses.clear(); - lastActiveWitnesses.addAll(currentWitnesses); - } else { - recordSrSetChange(currentWitnesses); + if (Metrics.enabled()) { + // Record transaction count distribution for all blocks (including empty blocks) + Metrics.histogramObserve(MetricKeys.Histogram.BLOCK_TRANSACTION_COUNT, txCount, + StringUtil.encode58Check(address)); + + // SR set change detection + long nextMaintenanceTime = dbManager.getDynamicPropertiesStore().getNextMaintenanceTime(); + if (lastNextMaintenanceTime == -1) { + lastNextMaintenanceTime = nextMaintenanceTime; + lastActiveWitnesses.addAll(chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() + .stream().map(w -> StringUtil.encode58Check(w.toByteArray())) + .collect(Collectors.toSet())); + } else if (nextMaintenanceTime != lastNextMaintenanceTime) { + Set currentWitnesses = + chainBaseManager.getWitnessScheduleStore().getActiveWitnesses() + .stream() + .map(w -> StringUtil.encode58Check(w.toByteArray())) + .collect(Collectors.toSet()); + if (nextMaintenanceTime < lastNextMaintenanceTime) { + // Fork rollback detected — reinitialize instead of diffing + lastActiveWitnesses.clear(); + lastActiveWitnesses.addAll(currentWitnesses); + } else { + recordSrSetChange(currentWitnesses); + } + lastNextMaintenanceTime = nextMaintenanceTime; } - lastNextMaintenanceTime = nextMaintenanceTime; } }