From fc8e45138e7e98eb08994fe877f666af72c3834d Mon Sep 17 00:00:00 2001 From: Brian Martin Date: Thu, 4 Nov 2021 09:02:58 -0700 Subject: [PATCH] add command line options to rust servers (#363) Adds CLI arguments to Rust Pelikan Backends to display usage, version, and stats information. Partially addresses #361 --- Cargo.lock | 2 + src/rust/core/server/src/lib.rs | 1 + src/rust/core/server/src/threads/admin.rs | 2 +- src/rust/core/server/src/threads/mod.rs | 2 +- src/rust/server/pingserver/Cargo.toml | 1 + src/rust/server/pingserver/src/main.rs | 66 ++++++++++++++++++++++- src/rust/server/segcache/Cargo.toml | 1 + src/rust/server/segcache/src/main.rs | 65 +++++++++++++++++++++- 8 files changed, 135 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cca30b376..8e127e7a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -786,6 +786,7 @@ name = "pingserver" version = "0.1.0" dependencies = [ "backtrace", + "clap", "config", "criterion", "entrystore", @@ -1133,6 +1134,7 @@ name = "segcache" version = "0.1.0" dependencies = [ "backtrace", + "clap", "config", "criterion", "entrystore", diff --git a/src/rust/core/server/src/lib.rs b/src/rust/core/server/src/lib.rs index 556970c99..6d991a3a4 100644 --- a/src/rust/core/server/src/lib.rs +++ b/src/rust/core/server/src/lib.rs @@ -96,6 +96,7 @@ mod process; mod threads; pub use process::{Process, ProcessBuilder}; +pub use threads::PERCENTILES; use metrics::{static_metrics, Counter}; diff --git a/src/rust/core/server/src/threads/admin.rs b/src/rust/core/server/src/threads/admin.rs index b217293b7..a8ab668c6 100644 --- a/src/rust/core/server/src/threads/admin.rs +++ b/src/rust/core/server/src/threads/admin.rs @@ -70,7 +70,7 @@ pub struct Admin { log_drain: Box, } -static PERCENTILES: &[(&str, f64)] = &[ +pub static PERCENTILES: &[(&str, f64)] = &[ ("p25", 25.0), ("p50", 50.0), ("p75", 75.0), diff --git a/src/rust/core/server/src/threads/mod.rs b/src/rust/core/server/src/threads/mod.rs index 0f54c66af..f81fc1a91 100644 --- a/src/rust/core/server/src/threads/mod.rs +++ b/src/rust/core/server/src/threads/mod.rs @@ -7,7 +7,7 @@ mod listener; mod traits; mod worker; -pub use admin::Admin; +pub use admin::{Admin, PERCENTILES}; pub use listener::Listener; pub use traits::EventLoop; pub use worker::{MultiWorker, SingleWorker, StorageWorker}; diff --git a/src/rust/server/pingserver/Cargo.toml b/src/rust/server/pingserver/Cargo.toml index a3e7ea779..5403c0a11 100644 --- a/src/rust/server/pingserver/Cargo.toml +++ b/src/rust/server/pingserver/Cargo.toml @@ -33,6 +33,7 @@ harness = false [dependencies] backtrace = "0.3.56" +clap = "2.33.3" config = { path = "../../config" } entrystore = { path = "../../entrystore" } logger = { path = "../../logger" } diff --git a/src/rust/server/pingserver/src/main.rs b/src/rust/server/pingserver/src/main.rs index f9b51bf77..a36c8a52d 100644 --- a/src/rust/server/pingserver/src/main.rs +++ b/src/rust/server/pingserver/src/main.rs @@ -6,8 +6,11 @@ extern crate logger; use backtrace::Backtrace; +use clap::{App, Arg}; use config::PingserverConfig; +use metrics::*; use pelikan_pingserver_rs::Pingserver; +use server::PERCENTILES; fn main() { // custom panic hook to terminate whole process after unwinding @@ -17,8 +20,69 @@ fn main() { std::process::exit(101); })); + // parse command line options + let matches = App::new(env!("CARGO_BIN_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .version_short("v") + .long_about( + "A rust implementation of, arguably, the most over-engineered ping \ + server.\n\n\ + The purpose is to demonstrate how to create an otherwise minimal \ + service with the libraries and modules provied by Pelikan, which \ + meets stringent requirements on latencies, observability, \ + configurability, and other valuable traits in a typical production \ + environment.", + ) + .arg( + Arg::with_name("stats") + .short("s") + .long("stats") + .help("List all metrics in stats") + .takes_value(false), + ) + .arg( + Arg::with_name("CONFIG") + .help("Server configuration file") + .index(1), + ) + .get_matches(); + + if matches.is_present("stats") { + println!("{:<31} {:<15} DESCRIPTION", "NAME", "TYPE"); + + let mut metrics = Vec::new(); + + for metric in &metrics::rustcommon_metrics::metrics() { + let any = match metric.as_any() { + Some(any) => any, + None => { + continue; + } + }; + + if any.downcast_ref::().is_some() { + metrics.push(format!("{:<31} counter", metric.name())); + } else if any.downcast_ref::().is_some() { + metrics.push(format!("{:<31} gauge", metric.name())); + } else if any.downcast_ref::().is_some() { + for (label, _) in PERCENTILES { + let name = format!("{}_{}", metric.name(), label); + metrics.push(format!("{:<31} percentile", name)); + } + } else { + continue; + } + } + + metrics.sort(); + for metric in metrics { + println!("{}", metric); + } + std::process::exit(0); + } + // load config from file - let config = if let Some(file) = std::env::args().nth(1) { + let config = if let Some(file) = matches.value_of("CONFIG") { debug!("loading config: {}", file); match PingserverConfig::load(&file) { Ok(c) => c, diff --git a/src/rust/server/segcache/Cargo.toml b/src/rust/server/segcache/Cargo.toml index 73c5a17a1..ea1b1e5c8 100644 --- a/src/rust/server/segcache/Cargo.toml +++ b/src/rust/server/segcache/Cargo.toml @@ -38,6 +38,7 @@ debug = ["entrystore/debug"] [dependencies] backtrace = "0.3.56" +clap = "2.33.3" config = { path = "../../config" } entrystore = { path = "../../entrystore" } logger = { path = "../../logger" } diff --git a/src/rust/server/segcache/src/main.rs b/src/rust/server/segcache/src/main.rs index 872cddbfc..14e51ba7d 100644 --- a/src/rust/server/segcache/src/main.rs +++ b/src/rust/server/segcache/src/main.rs @@ -6,8 +6,11 @@ extern crate logger; use backtrace::Backtrace; +use clap::{App, Arg}; use config::SegcacheConfig; +use metrics::*; use pelikan_segcache_rs::Segcache; +use server::PERCENTILES; fn main() { // custom panic hook to terminate whole process after unwinding @@ -17,10 +20,68 @@ fn main() { std::process::exit(101); })); + // parse command line options + let matches = App::new(env!("CARGO_BIN_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .version_short("v") + .long_about( + "One of the unified cache backends implemented in Rust. It \ + uses segment-based storage to cache key/val pairs. It speaks the \ + memcached ASCII protocol and supports some ASCII memcached \ + commands.", + ) + .arg( + Arg::with_name("stats") + .short("s") + .long("stats") + .help("List all metrics in stats") + .takes_value(false), + ) + .arg( + Arg::with_name("CONFIG") + .help("Server configuration file") + .index(1), + ) + .get_matches(); + + if matches.is_present("stats") { + println!("{:<31} {:<15} DESCRIPTION", "NAME", "TYPE"); + + let mut metrics = Vec::new(); + + for metric in &metrics::rustcommon_metrics::metrics() { + let any = match metric.as_any() { + Some(any) => any, + None => { + continue; + } + }; + + if any.downcast_ref::().is_some() { + metrics.push(format!("{:<31} counter", metric.name())); + } else if any.downcast_ref::().is_some() { + metrics.push(format!("{:<31} gauge", metric.name())); + } else if any.downcast_ref::().is_some() { + for (label, _) in PERCENTILES { + let name = format!("{}_{}", metric.name(), label); + metrics.push(format!("{:<31} percentile", name)); + } + } else { + continue; + } + } + + metrics.sort(); + for metric in metrics { + println!("{}", metric); + } + std::process::exit(0); + } + // load config from file - let config = if let Some(file) = std::env::args().nth(1) { + let config = if let Some(file) = matches.value_of("CONFIG") { debug!("loading config: {}", file); - match SegcacheConfig::load(&file) { + match SegcacheConfig::load(file) { Ok(c) => c, Err(e) => { error!("{}", e);