use commonware_runtime::{
telemetry::metrics::{
histogram::{ScopedTimer, Timed},
Counter, Gauge, GaugeExt as _, MetricsExt as _,
},
Clock, Metrics as RuntimeMetrics,
};
use std::sync::Arc;
pub(super) struct Metrics<E: Clock> {
size: Gauge,
pruning_boundary: Gauge,
retained: Gauge,
tail_items: Gauge,
pub append_calls: Counter,
append_duration: Timed,
pub append_many_calls: Counter,
append_many_duration: Timed,
pub append_prepared_calls: Counter,
append_prepared_duration: Timed,
pub read_calls: Counter,
read_duration: Timed,
pub read_many_calls: Counter,
read_many_duration: Timed,
pub cache_hits: Counter,
pub cache_misses: Counter,
pub items_read: Counter,
pub commit_calls: Counter,
commit_duration: Timed,
pub sync_calls: Counter,
sync_duration: Timed,
clock: Arc<E>,
}
impl<E: RuntimeMetrics + Clock> Metrics<E> {
pub(super) fn new(context: E) -> Self {
Self {
size: context.gauge("size", "Logical end position of the journal"),
pruning_boundary: context.gauge("pruning_boundary", "Oldest readable item position"),
retained: context.gauge("retained", "Number of readable items retained"),
tail_items: context.gauge(
"tail_items",
"Items in the blob containing the newest retained item",
),
append_calls: context.counter("append_calls", "Number of single-item append calls"),
append_duration: Timed::register(
&context,
"append_duration",
"Duration of single-item append calls",
),
append_many_calls: context.counter("append_many_calls", "Number of append-many calls"),
append_many_duration: Timed::register(
&context,
"append_many_duration",
"Duration of append-many calls",
),
append_prepared_calls: context.counter(
"append_prepared_calls",
"Number of pre-encoded batch append calls",
),
append_prepared_duration: Timed::register(
&context,
"append_prepared_duration",
"Duration of pre-encoded batch append calls",
),
read_calls: context.counter("read_calls", "Number of single-item read calls"),
read_duration: Timed::register(
&context,
"read_duration",
"Duration of single-item read calls that miss the page cache",
),
read_many_calls: context
.counter("read_many_calls", "Number of non-empty batch read calls"),
read_many_duration: Timed::register(
&context,
"read_many_duration",
"Duration of non-empty batch read calls",
),
cache_hits: context.counter("cache_hits", "Number of items served without a blob read"),
cache_misses: context.counter("cache_misses", "Number of items requiring a blob read"),
items_read: context.counter(
"items_read",
"Number of items returned by point reads, batch reads, and sync probes",
),
commit_calls: context.counter("commit_calls", "Number of commit calls"),
commit_duration: Timed::register(
&context,
"commit_duration",
"Duration of commit calls",
),
sync_calls: context.counter("sync_calls", "Number of sync calls"),
sync_duration: Timed::register(
&context,
"sync_duration",
"Duration of full sync calls",
),
clock: Arc::new(context),
}
}
}
impl<E: Clock> Metrics<E> {
pub(super) fn append_timer(&self) -> ScopedTimer<E> {
self.append_duration.scoped(&self.clock)
}
pub(super) fn append_many_timer(&self) -> ScopedTimer<E> {
self.append_many_duration.scoped(&self.clock)
}
pub(super) fn append_prepared_timer(&self) -> ScopedTimer<E> {
self.append_prepared_duration.scoped(&self.clock)
}
pub(super) fn read_timer(&self) -> ScopedTimer<E> {
self.read_duration.scoped(&self.clock)
}
pub(super) fn read_many_timer(&self) -> ScopedTimer<E> {
self.read_many_duration.scoped(&self.clock)
}
pub(super) fn commit_timer(&self) -> ScopedTimer<E> {
self.commit_duration.scoped(&self.clock)
}
pub(super) fn sync_timer(&self) -> ScopedTimer<E> {
self.sync_duration.scoped(&self.clock)
}
pub(super) fn update(&self, size: u64, pruning_boundary: u64, items_per_blob: u64) {
let _ = self.size.try_set(size);
let _ = self.pruning_boundary.try_set(pruning_boundary);
let _ = self.retained.try_set(size.saturating_sub(pruning_boundary));
let tail_items = if size == pruning_boundary {
0
} else {
let tail_start = ((size - 1) / items_per_blob) * items_per_blob;
size - pruning_boundary.max(tail_start)
};
let _ = self.tail_items.try_set(tail_items);
}
}