196 lines
8.1 KiB
Rust

//! The sequencer dashboard: chain progress, block timings, mempool and
//! transaction outcomes.
#![expect(
clippy::non_ascii_literal,
reason = "legend separators use `·` intentionally, matching the rendered Grafana labels"
)]
use dashboard_gen::{
Color, Dashboard, FieldOverride, GradientMode, Panel, Target, Thresholds, Unit, avg,
percentile_legend, percentile_variable, rate_per_min, selected_percentile,
};
const PERCENTILES: &[u32] = &[50, 90, 95, 99];
const DEFAULT_PERCENTILE: u32 = 95;
pub fn dashboard() -> Dashboard {
Dashboard::new("Sequencer", "sequencer")
.tag("sequencer")
.variable(percentile_variable(PERCENTILES, DEFAULT_PERCENTILE))
.row(
7,
[
Panel::stat("Chain height")
.width(6)
.unit(Unit::Short)
.decimals(0)
.color(Color::fixed("blue"))
.target(
Target::new(sequencer_core_metrics::names::CHAIN_HEIGHT).legend("height"),
),
Panel::stat("Blocks produced by this sequencer since startup")
.width(6)
.unit(Unit::Short)
.decimals(0)
.color(Color::fixed("green"))
.target(
Target::new(sequencer_core_metrics::names::BLOCKS_PRODUCED_TOTAL)
.legend("produced"),
),
Panel::timeseries("Block production rate")
.width(12)
.unit(Unit::Short)
.target(rate_per_min(
sequencer_core_metrics::names::BLOCKS_PRODUCED_TOTAL,
"produced · blocks/min",
))
.with_override(
FieldOverride::by_name("produced · blocks/min").color(Color::fixed("green")),
),
],
)
.row(
9,
[Panel::timeseries("Block creation time")
.width(24)
.unit(Unit::Seconds)
.target(selected_percentile(
sequencer_core_metrics::names::BLOCK_CREATION_TIME,
&[],
&percentile_legend(),
))
.target(avg(sequencer_core_metrics::names::BLOCK_CREATION_TIME))
.with_override(
FieldOverride::by_name("avg")
.dashed_line()
.color(Color::fixed("text")),
)],
)
.row(
9,
[
Panel::timeseries("Transaction application time")
.width(12)
.unit(Unit::Seconds)
.target(selected_percentile(
sequencer_core_metrics::names::MEMPOOL_TRANSACTION_APPLICATION_TIME,
&["kind", "origin", "status"],
"{{kind}} · {{origin}} · {{status}}",
)),
Panel::timeseries("Transactions per block")
.width(12)
.unit(Unit::Short)
.target(selected_percentile(
sequencer_core_metrics::names::TRANSACTIONS_PER_BLOCK,
&[],
&percentile_legend(),
))
.target(avg(sequencer_core_metrics::names::TRANSACTIONS_PER_BLOCK))
.with_override(
FieldOverride::by_name("avg")
.dashed_line()
.color(Color::fixed("text")),
),
],
)
.row(
8,
[
Panel::gauge("Mempool utilization")
.width(6)
.unit(Unit::Percent)
.decimals(1)
.min(0.0)
.max(100.0)
.thresholds(
Thresholds::base("green")
.step(70.0, "orange")
.step(90.0, "red"),
)
.target(
Target::new(format!(
"100 * {size} / {max_size}",
size = sequencer_core_metrics::names::MEMPOOL_SIZE,
max_size = sequencer_core_metrics::names::MEMPOOL_MAX_SIZE,
))
.legend("utilization"),
),
Panel::timeseries("Mempool size vs capacity")
.width(18)
.unit(Unit::Short)
.span_nulls()
.min(0.0)
.target(
Target::new(sequencer_core_metrics::names::MEMPOOL_SIZE).legend("queued"),
)
.target(
Target::new(sequencer_core_metrics::names::MEMPOOL_MAX_SIZE)
.legend("capacity"),
)
.with_override(
FieldOverride::by_name("capacity")
.dashed_line()
.color(Color::fixed("red")),
)
.with_override(FieldOverride::by_name("queued").color(Color::fixed("blue"))),
],
)
.row(
8,
[
Panel::gauge("Failed transactions share")
.width(6)
.unit(Unit::Percent)
.decimals(2)
.min(0.0)
.max(100.0)
.thresholds(
Thresholds::base("green")
.step(1.0, "orange")
.step(5.0, "red"),
)
.target(
Target::new(format!(
// Both failure stages against the same submission base;
// `clamp_min` keeps an idle window (nothing submitted)
// reading as 0% instead of a division by zero.
"100 * (increase({before_mempool}[$__range]) + increase({in_mempool}[$__range])) / clamp_min(increase({submitted}[$__range]), 1)",
before_mempool = sequencer_service_metrics::names::BEFORE_MEMPOOL_FAILED_TRANSACTIONS_TOTAL,
in_mempool = sequencer_core_metrics::names::MEMPOOL_FAILED_TRANSACTIONS_TOTAL,
submitted = sequencer_service_metrics::names::SUBMITTED_TRANSACTIONS_TOTAL,
))
.legend("failed"),
),
Panel::timeseries("Submitted vs failed transactions (per minute)")
.width(18)
.unit(Unit::Short)
.min(0.0)
.fill_opacity(35)
.gradient_mode(GradientMode::Opacity)
.target(rate_per_min(
sequencer_service_metrics::names::SUBMITTED_TRANSACTIONS_TOTAL,
"submitted",
))
.target(rate_per_min(
sequencer_service_metrics::names::BEFORE_MEMPOOL_FAILED_TRANSACTIONS_TOTAL,
"failed · before mempool",
))
.target(rate_per_min(
sequencer_core_metrics::names::MEMPOOL_FAILED_TRANSACTIONS_TOTAL,
"failed · in mempool",
))
.with_override(
FieldOverride::by_name("submitted").color(Color::fixed("green")),
)
.with_override(
FieldOverride::by_name("failed · before mempool")
.color(Color::fixed("orange")),
)
.with_override(
FieldOverride::by_name("failed · in mempool").color(Color::fixed("red")),
),
],
)
}