use std::time::{Duration, Instant};
use linera_core::node::ValidatorNode;
use super::{
latency::Samples,
progress::Progress,
report::{PreflightReport, PreflightStatus},
rpc::timed,
};
const PING_COUNT: usize = 10;
pub struct PreflightOutcome {
pub report: PreflightReport,
pub version_info: Option<String>,
pub network_description: Option<serde_json::Value>,
}
pub async fn run<N: ValidatorNode>(
node: &N,
rpc_timeout: Duration,
progress: &Progress,
) -> PreflightOutcome {
let phase = progress.phase("L1 preflight", None);
phase.set_message("version, network, rtt");
let mut report = PreflightReport::default();
let version_info = match timed(rpc_timeout, node.get_version_info()).await {
Ok(v) => Some(format!("{v:?}")),
Err(e) => {
report.errors.push(format!("get_version_info: {e}"));
None
}
};
let network_description = match timed(rpc_timeout, node.get_network_description()).await {
Ok(nd) => serde_json::to_value(&nd).ok(),
Err(e) => {
report.errors.push(format!("get_network_description: {e}"));
None
}
};
let mut rtt = Samples::new();
for _ in 0..PING_COUNT {
let start = Instant::now();
match timed(rpc_timeout, node.get_version_info()).await {
Ok(_) => rtt.record_success(start.elapsed().as_secs_f64() * 1000.0),
Err(category) => rtt.record_error(category),
}
}
report.rtt_ms = rtt.summary();
report.status = if report.errors.is_empty() {
PreflightStatus::Ok
} else {
PreflightStatus::Fail
};
match report.status {
PreflightStatus::Ok => phase.finish_ok(),
PreflightStatus::Fail => phase.finish_fail(),
}
PreflightOutcome {
report,
version_info,
network_description,
}
}
pub(super) fn categorize(message: &str) -> &'static str {
let lower = message.to_ascii_lowercase();
if lower.contains("timeout") || lower.contains("timed out") {
"timeout"
} else if lower.contains("connection refused")
|| lower.contains("unavailable")
|| lower.contains("transport")
{
"unavailable"
} else {
"other"
}
}
#[cfg(test)]
mod tests {
use super::categorize;
#[test]
fn categorize_timeout() {
assert_eq!(categorize("request timeout"), "timeout");
assert_eq!(categorize("operation timed out"), "timeout");
}
#[test]
fn categorize_unavailable() {
assert_eq!(categorize("connection refused"), "unavailable");
assert_eq!(categorize("Status: Unavailable"), "unavailable");
assert_eq!(categorize("transport error"), "unavailable");
}
#[test]
fn categorize_other() {
assert_eq!(categorize("something weird"), "other");
}
}