• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 17068937103

19 Aug 2025 12:02PM UTC coverage: 66.968% (+0.07%) from 66.9%
17068937103

Pull #495

github

web-flow
Merge 68ce374a1 into 9d001c25c
Pull Request #495: feat(docs): Add Optional form sections

12592 of 18803 relevant lines covered (66.97%)

3115.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/rust/cardano-chain-follower/src/stats/rollback.rs
1
//! Rollback statistics.
2

3
use std::sync::{Arc, LazyLock, RwLock};
4

5
use cardano_blockchain_types::Network;
6
use dashmap::DashMap;
7
use serde::Serialize;
8
use strum::EnumIter;
9
use tracing::error;
10

11
/// Statistics related to a single depth of rollback
12
#[derive(Debug, Default, Clone, Serialize)]
13
pub struct Rollback {
14
    /// How deep was the rollback from tip.
15
    pub depth: u64,
16
    /// How many times has a rollback been this deep.
17
    pub count: u64,
18
}
19

20
/// Statistics for all our known rollback types
21
/// Rollback Vec is sorted by depth, ascending.
22
#[derive(Debug, Default, Clone, Serialize)]
23
pub struct Rollbacks {
24
    /// These are the ACTUAL rollbacks we did on our live-chain in memory.
25
    pub live: Vec<Rollback>,
26
    /// These are the rollbacks reported by the Peer Node, which may not == an actual
27
    /// rollback on our internal live chain.
28
    pub peer: Vec<Rollback>,
29
    /// These are the rollbacks synthesized for followers, based on their reading of the
30
    /// chain tip.
31
    pub follower: Vec<Rollback>,
32
}
33

34
/// The types of rollbacks we track for a chain.
35
#[derive(EnumIter, Eq, Ord, PartialEq, PartialOrd, Copy, Clone, Hash)]
36
#[allow(clippy::module_name_repetitions)]
37
pub enum RollbackType {
38
    /// Rollback on the in-memory live chain.
39
    LiveChain,
40
    /// Rollback signaled by the peer.
41
    Peer,
42
    /// Rollback synthesized for the Follower.
43
    Follower,
44
}
45

46
/// Individual rollback records.
47
type RollbackRecords = DashMap<u64, Rollback>;
48
/// Rollback Records per rollback type.
49
type RollbackTypeMap = DashMap<RollbackType, Arc<RwLock<RollbackRecords>>>;
50
/// Record of rollbacks.
51
type RollbackMap = DashMap<Network, RollbackTypeMap>;
52

53
/// Statistics of rollbacks detected per chain.
54
static ROLLBACKS_MAP: LazyLock<RollbackMap> = LazyLock::new(RollbackMap::new);
55

56
/// Get the actual rollback map for a chain.
57
fn lookup_rollback_map(
×
58
    chain: Network,
×
59
    rollback: RollbackType,
×
60
) -> Arc<RwLock<RollbackRecords>> {
×
61
    let Some(chain_rollback_map) = ROLLBACKS_MAP.get(&chain) else {
×
62
        let res = Arc::new(RwLock::new(RollbackRecords::new()));
×
63
        let chain_rollback_map = DashMap::new();
×
64
        chain_rollback_map.insert(rollback, res.clone());
×
65
        ROLLBACKS_MAP.insert(chain, chain_rollback_map);
×
66
        return res;
×
67
    };
68

69
    let Some(rollback_map) = chain_rollback_map.get(&rollback) else {
×
70
        let res = Arc::new(RwLock::new(RollbackRecords::new()));
×
71
        chain_rollback_map.insert(rollback, res.clone());
×
72
        return res;
×
73
    };
74
    rollback_map.clone()
×
75
}
×
76

77
/// Extract the current rollback stats as a vec.
78
pub(crate) fn rollbacks(
×
79
    chain: Network,
×
80
    rollback: RollbackType,
×
81
) -> Vec<Rollback> {
×
82
    let rollback_map = lookup_rollback_map(chain, rollback);
×
83

84
    let Ok(rollback_values) = rollback_map.read() else {
×
85
        error!("Rollback stats LOCK Poisoned, should not happen.");
×
86
        return vec![];
×
87
    };
88

89
    let mut rollbacks = Vec::new();
×
90

91
    // Get all the rollback stats.
92
    for stat in rollback_values.iter() {
×
93
        rollbacks.push(stat.value().clone());
×
94
    }
×
95

96
    rollbacks
×
97
}
×
98

99
/// Reset ALL the rollback stats for a given blockchain.
100
pub(crate) fn rollbacks_reset(
×
101
    chain: Network,
×
102
    rollback: RollbackType,
×
103
) -> Vec<Rollback> {
×
104
    let rollback_map = lookup_rollback_map(chain, rollback);
×
105

106
    let Ok(rollbacks) = rollback_map.write() else {
×
107
        error!("Rollback stats LOCK Poisoned, should not happen.");
×
108
        return vec![];
×
109
    };
110

111
    rollbacks.clear();
×
112

113
    Vec::new()
×
114
}
×
115

116
/// Count a rollback
117
pub(crate) fn rollback(
×
118
    chain: Network,
×
119
    rollback: RollbackType,
×
120
    depth: u64,
×
121
) {
×
122
    let rollback_map = lookup_rollback_map(chain, rollback);
×
123

124
    let Ok(rollbacks) = rollback_map.write() else {
×
125
        error!("Rollback stats LOCK Poisoned, should not happen.");
×
126
        return;
×
127
    };
128

129
    let mut value = match rollbacks.get(&depth) {
×
130
        Some(value_entry) => (*value_entry.value()).clone(),
×
131
        None => Rollback { depth, count: 0 },
×
132
    };
133

134
    value.count = value.count.saturating_add(1);
×
135

136
    let _unused = rollbacks.insert(depth, value);
×
137
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc