use crate::{
ids::SystemMetricKind,
ops::{prelude::*, runtime::metrics::system::SystemMetrics},
};
use std::{cell::RefCell, collections::HashMap};
thread_local! {
static INTER_CANISTER_CALL_METRICS: RefCell<HashMap<InterCanisterCallMetricKey, u64>> =
RefCell::new(HashMap::new());
}
#[derive(Clone)]
pub struct InterCanisterCallMetricsSnapshot {
pub entries: Vec<(InterCanisterCallMetricKey, u64)>,
}
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct InterCanisterCallMetricKey {
pub target: Principal,
pub method: String,
}
pub struct InterCanisterCallMetrics;
impl InterCanisterCallMetrics {
fn increment(target: Principal, method: &str) {
INTER_CANISTER_CALL_METRICS.with_borrow_mut(|counts| {
let key = InterCanisterCallMetricKey {
target,
method: method.to_string(),
};
let entry = counts.entry(key).or_insert(0);
*entry = entry.saturating_add(1);
});
}
pub fn record_call(target: impl Into<Principal>, method: &str) {
let target: Principal = target.into();
SystemMetrics::increment(SystemMetricKind::CanisterCall);
Self::increment(target, method);
}
#[must_use]
pub fn snapshot() -> InterCanisterCallMetricsSnapshot {
let entries = INTER_CANISTER_CALL_METRICS
.with_borrow(std::clone::Clone::clone)
.into_iter()
.collect();
InterCanisterCallMetricsSnapshot { entries }
}
#[cfg(test)]
pub fn reset() {
INTER_CANISTER_CALL_METRICS.with_borrow_mut(HashMap::clear);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ops::runtime::metrics::system::SystemMetrics;
fn snapshot_map() -> HashMap<InterCanisterCallMetricKey, u64> {
InterCanisterCallMetrics::snapshot()
.entries
.into_iter()
.collect()
}
#[test]
fn inter_canister_call_metrics_track_target_and_method() {
InterCanisterCallMetrics::reset();
let t1 = Principal::from_slice(&[1; 29]);
let t2 = Principal::from_slice(&[2; 29]);
InterCanisterCallMetrics::increment(t1, "foo");
InterCanisterCallMetrics::increment(t1, "foo");
InterCanisterCallMetrics::increment(t1, "bar");
InterCanisterCallMetrics::increment(t2, "foo");
let map = snapshot_map();
assert_eq!(
map.get(&InterCanisterCallMetricKey {
target: t1,
method: "foo".to_string()
}),
Some(&2)
);
assert_eq!(
map.get(&InterCanisterCallMetricKey {
target: t1,
method: "bar".to_string()
}),
Some(&1)
);
assert_eq!(
map.get(&InterCanisterCallMetricKey {
target: t2,
method: "foo".to_string()
}),
Some(&1)
);
assert_eq!(map.len(), 3);
}
#[test]
fn record_call_updates_inter_canister_call_and_system_metrics() {
InterCanisterCallMetrics::reset();
SystemMetrics::reset();
let target = Principal::from_slice(&[3; 29]);
InterCanisterCallMetrics::record_call(target, "canic_sync");
InterCanisterCallMetrics::record_call(target, "canic_sync");
let map = snapshot_map();
assert_eq!(
map.get(&InterCanisterCallMetricKey {
target,
method: "canic_sync".to_string()
}),
Some(&2)
);
let system: HashMap<_, _> = SystemMetrics::snapshot().into_iter().collect();
assert_eq!(system.get(&SystemMetricKind::CanisterCall), Some(&2));
}
}