Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance GitHub polling and error handling in server #16

Merged
merged 1 commit into from
Feb 20, 2025
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
504 changes: 496 additions & 8 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ out --output-dir performance_results

```bash
git clone <repository-url>
cd iggy-yew
cd iggy-bench-dashboard
```

2. Run the development script:
Expand Down
65 changes: 54 additions & 11 deletions frontend/src/components/chart/plot_trend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,48 +72,61 @@ fn create_latency_trend_chart(data: &[BenchmarkReportLight], is_dark: bool) -> C
let mut consumer_p99_latencies = Vec::new();
let mut consumer_p999_latencies = Vec::new();

let mut producing_consumer_avg_latencies = Vec::new();
let mut producing_consumer_p95_latencies = Vec::new();
let mut producing_consumer_p99_latencies = Vec::new();
let mut producing_consumer_p999_latencies = Vec::new();

let mut chart = IggyChart::new(&title, &subtext, is_dark, true)
.with_category_x_axis("Version", gitrefs)
.with_y_axis("Latency [ms]");

for report in data {
let mut send_summary: Option<&BenchmarkGroupMetricsSummary> = None;
let mut poll_summary: Option<&BenchmarkGroupMetricsSummary> = None;
let mut producers_summary: Option<&BenchmarkGroupMetricsSummary> = None;
let mut consumers_summary: Option<&BenchmarkGroupMetricsSummary> = None;
let mut producing_consumers_summary: Option<&BenchmarkGroupMetricsSummary> = None;

for group_metric in &report.group_metrics {
match group_metric.summary.kind {
GroupMetricsKind::Producers => {
send_summary = Some(&group_metric.summary);
producers_summary = Some(&group_metric.summary);
}
GroupMetricsKind::Consumers => {
poll_summary = Some(&group_metric.summary);
consumers_summary = Some(&group_metric.summary);
}
GroupMetricsKind::ProducersAndConsumers => {
producing_consumers_summary = Some(&group_metric.summary);
}
// GroupMetricsKind::ProducersAndConsumers => {
// for now ignored
// }
_ => {}
}
}

if let Some(summary) = send_summary {
if let Some(summary) = producers_summary {
producer_avg_latencies.push(summary.average_latency_ms);
producer_p95_latencies.push(summary.average_p95_latency_ms);
producer_p99_latencies.push(summary.average_p99_latency_ms);
producer_p999_latencies.push(summary.average_p999_latency_ms);
}

if let Some(summary) = poll_summary {
if let Some(summary) = consumers_summary {
consumer_avg_latencies.push(summary.average_latency_ms);
consumer_p95_latencies.push(summary.average_p95_latency_ms);
consumer_p99_latencies.push(summary.average_p99_latency_ms);
consumer_p999_latencies.push(summary.average_p999_latency_ms);
}

if let Some(summary) = producing_consumers_summary {
producing_consumer_avg_latencies.push(summary.average_latency_ms);
producing_consumer_p95_latencies.push(summary.average_p95_latency_ms);
producing_consumer_p99_latencies.push(summary.average_p99_latency_ms);
producing_consumer_p999_latencies.push(summary.average_p999_latency_ms);
}
}

chart = if !producer_avg_latencies.is_empty() {
chart
.add_series(
"Producer Average Latency",
"Producer Avg Latency",
producer_avg_latencies,
Symbol::Circle,
"#5470c6",
Expand Down Expand Up @@ -143,7 +156,7 @@ fn create_latency_trend_chart(data: &[BenchmarkReportLight], is_dark: bool) -> C
chart = if !consumer_avg_latencies.is_empty() {
chart
.add_series(
"Consumer Average Latency",
"Consumer Avg Latency",
consumer_avg_latencies,
Symbol::Circle,
"#73c0de",
Expand All @@ -170,6 +183,36 @@ fn create_latency_trend_chart(data: &[BenchmarkReportLight], is_dark: bool) -> C
chart
};

chart = if !producing_consumer_avg_latencies.is_empty() {
chart
.add_series(
"Producing Consumers Avg Latency",
producing_consumer_avg_latencies,
Symbol::Circle,
"#73c0de",
)
.add_series(
"Producing Consumers P95 Latency",
producing_consumer_p95_latencies,
Symbol::Triangle,
"#3ba272",
)
.add_series(
"Producing Consumers P99 Latency",
producing_consumer_p99_latencies,
Symbol::Diamond,
"#fc8452",
)
.add_series(
"Producing Consumers P999 Latency",
producing_consumer_p999_latencies,
Symbol::Rect,
"#ea7ccc",
)
} else {
chart
};

chart.inner.tooltip(Tooltip::new().trigger(Trigger::Axis))
}

Expand Down
7 changes: 5 additions & 2 deletions runner/src/app/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod local_benchmark_runner;
mod utils;
use anyhow::Result;
use anyhow::{Context, Result};
use dircpy::copy_dir;
use local_benchmark_runner::LocalBenchmarkRunner;
use tracing::info;
Expand Down Expand Up @@ -36,7 +36,10 @@ impl IggyBenchRunnerApp {
for commit in commits {
info!("Processing commit: {}", commit);
local_benchmark.checkout_to_gitref(&commit)?;
local_benchmark.run_benchmark().await?;
local_benchmark
.run_benchmark()
.await
.context("Failed to run benchmark")?;
}

let source_dir = repo_path + "/performance_results";
Expand Down
9 changes: 5 additions & 4 deletions runner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ use tracing_subscriber::{

#[tokio::main]
async fn main() -> Result<()> {
// Parse arguments first
let args = IggyBenchRunnerArgs::parse();

// Initialize tracing
let env_filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&args.log_level));

Expand All @@ -28,7 +26,8 @@ async fn main() -> Result<()> {
.try_init()
.unwrap();

// Validate configuration
info!("Starting IggyBenchRunner with args: {:?}", args);

if let Err(e) = args.validate() {
error!("Configuration error: {}", e);
std::process::exit(1);
Expand All @@ -38,5 +37,7 @@ async fn main() -> Result<()> {
info!("Log level: {}", args.log_level);

let app = IggyBenchRunnerApp::new(args)?;
app.run().await
let res = app.run().await;
info!("Benchmark run result: {:?}", res);
res
}
12 changes: 0 additions & 12 deletions server/Cargo.lock

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

3 changes: 3 additions & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "time"] }
uuid = { version = "1.13.1", features = ["serde"] }
zip = { version = "2.2.2", features = ["deflate"] }
walkdir = "2.5.0"
tempfile = "3.17.1"
octocrab = "0.43.0"
file-operation = "0.4.1"
96 changes: 96 additions & 0 deletions server/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use clap::{error::ErrorKind, CommandFactory, Parser, Subcommand};
use serde::Deserialize;
use std::path::PathBuf;

#[derive(Debug, Subcommand, Deserialize)]
pub enum PollGithub {
PollGithub(PollGithubArgs),
}

#[derive(Debug, Parser, Deserialize)]
pub struct PollGithubArgs {
/// How often to poll GitHub for new artifacts, in seconds
#[arg(short, long, default_value = "60")]
pub interval_seconds: u64,

/// Branch to filter artifacts by
#[arg(short, long, default_value = "master")]
pub branch: String,
}

#[derive(Debug, Deserialize, Parser)]
#[command(author, version, about, long_about = None)]
pub struct IggyBenchDashboardServerArgs {
/// Server host address
#[arg(long, default_value = "127.0.0.1")]
pub host: String,

/// Server port
#[arg(long, default_value_t = 8061)]
pub port: u16,

/// Directory containing performance results
#[arg(long, default_value = "./performance_results")]
pub results_dir: PathBuf,

/// Log level (trace, debug, info, warn, error)
#[arg(long, default_value = "info")]
pub log_level: String,

/// Allowed CORS origins (comma-separated)
#[arg(long, default_value = "*")]
pub cors_origins: String,

/// Poll GitHub for new artifacts
#[command(subcommand)]
pub github: Option<PollGithub>,
}

impl IggyBenchDashboardServerArgs {
pub fn parse() -> Self {
Self::parse_from(std::env::args())
}

pub fn validate(&self) {
let mut cmd = IggyBenchDashboardServerArgs::command();
if !self.results_dir.exists() {
cmd.error(
ErrorKind::InvalidValue,
format!(
"Results directory does not exist: {}",
self.results_dir.display()
),
)
.exit();
}
if !self.results_dir.is_dir() {
cmd.error(
ErrorKind::InvalidValue,
format!(
"Results path is not a directory: {}",
self.results_dir.display()
),
)
.exit();
}

if self.github.is_some() && std::env::var("GITHUB_TOKEN").is_err() {
cmd.error(
ErrorKind::InvalidValue,
"GITHUB_TOKEN env variable not set, but GitHub polling enabled",
)
.exit();
}
}

pub fn server_addr(&self) -> String {
format!("{}:{}", self.host, self.port)
}

pub fn cors_origins_list(&self) -> Vec<String> {
self.cors_origins
.split(',')
.map(|s| s.trim().to_string())
.collect()
}
}
Loading
Loading