Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ url = "2.5.4"
alloy-hardforks = "0.4.0"
alloy-chains = "0.2"
criterion = { version = "0.8.2", features = ["async_tokio"] }
metrics-util = "0.20"
signet-bundle = "0.16.0-rc.11"

[[bench]]
Expand Down
104 changes: 102 additions & 2 deletions src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,30 @@
//! use bare `counter!` / `histogram!` macros directly. This prevents
//! metric-name typos and provides a single place to survey every metric the
//! builder emits.
use init4_bin_base::deps::metrics::{counter, describe_counter, describe_histogram, histogram};
use std::sync::LazyLock;
use init4_bin_base::deps::metrics::{
counter, describe_counter, describe_gauge, describe_histogram, gauge, histogram,
};
use std::{
sync::LazyLock,
time::{SystemTime, UNIX_EPOCH},
};

// ---------------------------------------------------------------------------
// Metric names and help text
// ---------------------------------------------------------------------------

// -- Chain ingress --

const ROLLUP_BLOCKS_SEEN: &str = "signet.builder.rollup_blocks_seen";
const ROLLUP_BLOCKS_SEEN_HELP: &str =
"Rollup-chain blocks observed by the builder. Advances even when no block is built.";

const LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP: &str = "signet.builder.last_rollup_block_seen_timestamp";
const LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP_HELP: &str =
"Unix seconds (wall clock) at which the builder most recently observed a rollup block.";

const ROLLUP_CHAIN_ID_LABEL: &str = "rollup_chain_id";

// -- Block building --

const BUILT_BLOCKS: &str = "signet.builder.built_blocks";
Expand Down Expand Up @@ -145,6 +162,10 @@ const PYLON_SIDECARS_SUBMITTED_HELP: &str = "Successful Pylon sidecar submission
// ---------------------------------------------------------------------------

static DESCRIPTIONS: LazyLock<()> = LazyLock::new(|| {
// Chain ingress
describe_counter!(ROLLUP_BLOCKS_SEEN, ROLLUP_BLOCKS_SEEN_HELP);
describe_gauge!(LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP, LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP_HELP);

// Block building
describe_counter!(BUILT_BLOCKS, BUILT_BLOCKS_HELP);
describe_histogram!(BUILT_BLOCKS_TX_COUNT, BUILT_BLOCKS_TX_COUNT_HELP);
Expand Down Expand Up @@ -204,6 +225,22 @@ pub(crate) fn init() {
LazyLock::force(&DESCRIPTIONS);
}

// ---------------------------------------------------------------------------
// Public API -- Chain ingress
// ---------------------------------------------------------------------------

/// Record that the builder has observed a new rollup-chain block, labeled by
/// `rollup_chain_id`.
pub(crate) fn record_rollup_block_seen(rollup_chain_id: u64) {
counter!(ROLLUP_BLOCKS_SEEN, ROLLUP_CHAIN_ID_LABEL => rollup_chain_id.to_string()).increment(1);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock before UNIX_EPOCH")
.as_secs_f64();
gauge!(LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP, ROLLUP_CHAIN_ID_LABEL => rollup_chain_id.to_string())
.set(now);
}

// ---------------------------------------------------------------------------
// Public API -- Block building
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -421,3 +458,66 @@ pub(crate) fn inc_pylon_submission_failures() {
pub(crate) fn inc_pylon_sidecars_submitted() {
counter!(PYLON_SIDECARS_SUBMITTED).increment(1);
}

#[cfg(test)]
mod tests {
use super::{
LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP, ROLLUP_BLOCKS_SEEN, ROLLUP_CHAIN_ID_LABEL,
record_rollup_block_seen,
};
use init4_bin_base::deps::metrics::{Label, with_local_recorder};
use metrics_util::{
MetricKind,
debugging::{DebugValue, DebuggingRecorder},
};
use std::time::{SystemTime, UNIX_EPOCH};

/// Verify that each call to `record_rollup_block_seen` advances the
/// `rollup_blocks_seen` counter by one and sets the
/// `last_rollup_block_seen_timestamp` gauge to (approximately) the current
/// wall-clock Unix time, with the chain id attached as a label.
#[test]
fn record_rollup_block_seen_advances_counter_and_gauge() {
const CHAIN_ID: u64 = 17_001;
const OBSERVATIONS: u64 = 3;

let recorder = DebuggingRecorder::new();
let snapshotter = recorder.snapshotter();

let before = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64();
with_local_recorder(&recorder, || {
for _ in 0..OBSERVATIONS {
record_rollup_block_seen(CHAIN_ID);
}
});
let after = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64();

let expected_label = Label::new(ROLLUP_CHAIN_ID_LABEL, CHAIN_ID.to_string());

let mut counter_value = None;
let mut gauge_value = None;
for (key, _, _, value) in snapshotter.snapshot().into_vec() {
let labels: Vec<_> = key.key().labels().cloned().collect();
assert!(
labels.contains(&expected_label),
"metric {} missing rollup_chain_id label, found: {labels:?}",
key.key().name()
);
match (key.kind(), key.key().name()) {
(MetricKind::Counter, ROLLUP_BLOCKS_SEEN) => counter_value = Some(value),
(MetricKind::Gauge, LAST_ROLLUP_BLOCK_SEEN_TIMESTAMP) => gauge_value = Some(value),
(kind, name) => panic!("unexpected metric in test scope: {name} ({kind:?})"),
}
}

assert_eq!(counter_value, Some(DebugValue::Counter(OBSERVATIONS)));
let Some(DebugValue::Gauge(ts)) = gauge_value else {
panic!("expected gauge value, got {gauge_value:?}");
};
let ts: f64 = ts.into();
assert!(
ts >= before && ts <= after,
"gauge timestamp {ts} not in observed window [{before}, {after}]"
);
}
}
2 changes: 2 additions & 0 deletions src/tasks/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ impl EnvTask {
drop(span);

while let Some(rollup_header) = rollup_headers.next().await {
crate::metrics::record_rollup_block_seen(self.config.constants.ru_chain_id());

let host_block_number =
self.config.constants.rollup_block_to_host_block_num(rollup_header.number);
let rollup_block_number = rollup_header.number;
Expand Down