@@ -23,7 +23,7 @@ use std::sync::RwLock;
2323use std:: time:: { Duration , Instant } ;
2424
2525use arc_consensus_types:: ConsensusParams ;
26- use arc_consensus_types:: { Address , ValidatorSet } ;
26+ use arc_consensus_types:: { Address , Height , Round , ValidatorSet } ;
2727use malachitebft_app_channel:: app:: metrics:: prometheus:: encoding:: {
2828 EncodeLabelSet , EncodeLabelValue , LabelValueEncoder ,
2929} ;
@@ -111,6 +111,9 @@ pub struct Inner {
111111 /// Number of blocks replayed from CL to EL during startup handshake
112112 handshake_replay_blocks : Gauge < u64 , AtomicU64 > ,
113113
114+ /// Number of consensus rounds that failed to decide before advancing to the next round
115+ consensus_round_missed : Family < RoundMissedLabel , Counter > ,
116+
114117 /// Internal state recording the previous validator set.
115118 /// Useful field to manage validators' metrics.
116119 /// This field is only accessible internally, and is not a metrics itself.
@@ -145,6 +148,7 @@ impl Inner {
145148 pending_proposal_parts_count : Gauge :: default ( ) ,
146149 consensus_params : Family :: default ( ) ,
147150 handshake_replay_blocks : Gauge :: default ( ) ,
151+ consensus_round_missed : Family :: default ( ) ,
148152 }
149153 }
150154}
@@ -280,6 +284,12 @@ impl AppMetrics {
280284 metrics. handshake_replay_blocks . clone ( ) ,
281285 ) ;
282286
287+ registry. register (
288+ "consensus_round_missed" ,
289+ "Number of consensus rounds that failed to decide before advancing to the next round" ,
290+ metrics. consensus_round_missed . clone ( ) ,
291+ ) ;
292+
283293 // Register version info as a separate Info metric
284294 let version_info = Info :: new ( VersionInfoLabel {
285295 version : arc_version:: SHORT_VERSION ,
@@ -468,6 +478,21 @@ impl AppMetrics {
468478 pub fn get_handshake_replay_blocks ( & self ) -> u64 {
469479 self . handshake_replay_blocks . get ( )
470480 }
481+
482+ /// Record that a consensus round failed to decide before advancing.
483+ pub fn inc_consensus_round_missed ( & self , proposer : Address , height : Height , round : Round ) {
484+ self . consensus_round_missed
485+ . get_or_create ( & RoundMissedLabel :: new ( proposer, height, round) )
486+ . inc ( ) ;
487+ }
488+
489+ /// Total number of missed consensus rounds since start.
490+ #[ cfg( test) ]
491+ pub fn get_consensus_round_missed_count ( & self , proposer : Address , height : Height , round : Round ) -> u64 {
492+ self . consensus_round_missed
493+ . get_or_create ( & RoundMissedLabel :: new ( proposer, height, round) )
494+ . get ( )
495+ }
471496}
472497
473498impl Default for AppMetrics {
@@ -505,6 +530,35 @@ impl AddressLabel {
505530 }
506531}
507532
533+ impl EncodeLabelValue for AsLabelValue < i64 > {
534+ fn encode ( & self , encoder : & mut LabelValueEncoder ) -> Result < ( ) , std:: fmt:: Error > {
535+ encoder. write_fmt ( format_args ! ( "{}" , self . 0 ) )
536+ }
537+ }
538+
539+ impl EncodeLabelValue for AsLabelValue < u64 > {
540+ fn encode ( & self , encoder : & mut LabelValueEncoder ) -> Result < ( ) , std:: fmt:: Error > {
541+ encoder. write_fmt ( format_args ! ( "{}" , self . 0 ) )
542+ }
543+ }
544+
545+ #[ derive( Clone , Copy , Debug , Hash , PartialEq , Eq , EncodeLabelSet ) ]
546+ struct RoundMissedLabel {
547+ proposer : AsLabelValue < Address > ,
548+ height : AsLabelValue < u64 > ,
549+ round : AsLabelValue < i64 > ,
550+ }
551+
552+ impl RoundMissedLabel {
553+ fn new ( proposer : Address , height : Height , round : Round ) -> Self {
554+ Self {
555+ proposer : AsLabelValue ( proposer) ,
556+ height : AsLabelValue ( height. as_u64 ( ) ) ,
557+ round : AsLabelValue ( round. as_i64 ( ) ) ,
558+ }
559+ }
560+ }
561+
508562#[ derive( Clone , Debug , Hash , PartialEq , Eq , EncodeLabelSet ) ]
509563struct ProcessMsgLabel {
510564 msg : & ' static str ,
@@ -641,4 +695,18 @@ mod tests {
641695 "Metrics should not contain 0x prefix: {buf}"
642696 ) ;
643697 }
698+
699+ #[ test]
700+ fn test_consensus_round_missed_and_blocks_proposed_counters ( ) {
701+ let metrics = AppMetrics :: new ( ) ;
702+ let proposer = Address :: new ( [ 0xAA ; 20 ] ) ;
703+
704+ metrics. inc_consensus_round_missed ( proposer, Height :: new ( 1 ) , Round :: new ( 0 ) ) ;
705+ metrics. inc_consensus_round_missed ( proposer, Height :: new ( 2 ) , Round :: new ( 0 ) ) ;
706+ metrics. inc_consensus_round_missed ( proposer, Height :: new ( 2 ) , Round :: new ( 1 ) ) ;
707+
708+ assert_eq ! ( metrics. get_consensus_round_missed_count( proposer, Height :: new( 1 ) , Round :: new( 0 ) ) , 1 ) ;
709+ assert_eq ! ( metrics. get_consensus_round_missed_count( proposer, Height :: new( 2 ) , Round :: new( 0 ) ) , 1 ) ;
710+ assert_eq ! ( metrics. get_consensus_round_missed_count( proposer, Height :: new( 2 ) , Round :: new( 1 ) ) , 1 ) ;
711+ }
644712}
0 commit comments