Skip to content

Commit 82b7e12

Browse files
consensus missed round metric
1 parent 2176637 commit 82b7e12

3 files changed

Lines changed: 87 additions & 1 deletion

File tree

crates/malachite-app/METRICS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ This application exposes Prometheus metrics on the `/metrics` endpoint. The foll
7474
- **`arc_malachite_app_height_restart_count`** (Counter)
7575
- **Description:** Number of times the consensus height has been restarted due to errors or recovery scenarios.
7676

77+
- **`arc_malachite_app_consensus_round_missed`** (Counter)
78+
- **Description:** Number of consensus rounds that failed to decide before advancing to the next round.
79+
- **Labels:**
80+
- `proposer`: Address of the validator that was proposer for the missed round.
81+
- `height`: Current height of the chain.
82+
- `round`: The round number that failed to decide.
83+
7784
- **`arc_malachite_app_sync_fell_behind_count`** (Counter)
7885
- **Description:** Number of times the node fell behind and transitioned from InSync to CatchingUp.
7986

crates/malachite-app/src/handlers/started_round.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ async fn on_started_round(
9797
assert!(round != Round::Nil, "Round cannot be Nil");
9898
assert!(round >= state.current_round, "Round cannot go backwards");
9999

100+
if round.as_i64() > 0 {
101+
let current_round = round.as_u32().expect("round is defined");
102+
let missed_round = Round::new(current_round - 1);
103+
let missed_proposer = state
104+
.ctx
105+
.proposer_selector
106+
.select_proposer(state.validator_set(), height, missed_round)
107+
.address;
108+
state.metrics().inc_consensus_round_missed(missed_proposer, height, missed_round);
109+
}
110+
100111
state.current_round = round;
101112
state.current_proposer = Some(proposer);
102113

crates/malachite-app/src/metrics/app.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::sync::RwLock;
2323
use std::time::{Duration, Instant};
2424

2525
use arc_consensus_types::ConsensusParams;
26-
use arc_consensus_types::{Address, ValidatorSet};
26+
use arc_consensus_types::{Address, Height, Round, ValidatorSet};
2727
use 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

473498
impl 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)]
509563
struct 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

Comments
 (0)