use std::{str::FromStr, time::Duration};
use anyhow::{Context, anyhow};
use clap::Args;
use humantime::parse_duration;
use crate::traits::shed::TypeExtractor;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ShedReason {
CPU,
Memory,
LoadAvg,
Latency,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ShedResponse<T> {
Inner(T),
Overload(ShedReason),
}
#[derive(Debug, Clone, Copy)]
pub struct SystemOptions {
pub cpu: Option<f64>,
pub memory: Option<f64>,
pub loadavg_1: Option<f64>,
pub loadavg_5: Option<f64>,
pub loadavg_15: Option<f64>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct TypeLatency<T>(pub T, pub Duration);
impl<T: FromStr> FromStr for TypeLatency<T>
where
T::Err: std::error::Error + Send + Sync + Sized + 'static,
{
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (rtype, lat) = s
.split_once(":")
.ok_or_else(|| anyhow!("incorrect format"))?;
let rtype = T::from_str(rtype).context("unknown request type")?;
let lat = parse_duration(lat).context("unable to parse latency")?;
Ok(Self(rtype, lat))
}
}
#[derive(Debug, Clone)]
pub struct ShardedOptions<T: TypeExtractor> {
pub extractor: T,
pub ewma_alpha: f64,
pub passthrough_count: u64,
pub latencies: Vec<TypeLatency<T::Type>>,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct ShedSystemCli {
#[clap(env, long, default_value = "0.8")]
pub shed_system_ewma: f64,
#[clap(env, long)]
pub shed_system_cpu: Option<f64>,
#[clap(env, long)]
pub shed_system_memory: Option<f64>,
#[clap(env, long)]
pub shed_system_load_avg_1: Option<f64>,
#[clap(env, long)]
pub shed_system_load_avg_5: Option<f64>,
#[clap(env, long)]
pub shed_system_load_avg_15: Option<f64>,
}
impl From<ShedSystemCli> for SystemOptions {
fn from(v: ShedSystemCli) -> Self {
Self {
cpu: v.shed_system_cpu,
memory: v.shed_system_memory,
loadavg_1: v.shed_system_load_avg_1,
loadavg_5: v.shed_system_load_avg_5,
loadavg_15: v.shed_system_load_avg_15,
}
}
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct ShedShardedCli<T: FromStr + Clone + Send + Sync + 'static>
where
T::Err: std::error::Error + Send + Sync + 'static,
{
#[clap(env, long, default_value = "0.8")]
pub shed_sharded_ewma: f64,
#[clap(env, long, default_value = "1000")]
pub shed_sharded_passthrough: u64,
#[clap(env, long, value_delimiter = ',')]
pub shed_sharded_latency: Vec<TypeLatency<T>>,
}
#[cfg(test)]
mod test {
use std::time::Duration;
use clap::Parser;
use super::*;
use crate::types::RequestType;
#[test]
fn test_type_latency() {
assert!(TypeLatency::<RequestType>::from_str("foo").is_err());
assert!(TypeLatency::<RequestType>::from_str(":").is_err());
assert!(TypeLatency::<RequestType>::from_str("foo:100ms").is_err());
assert!(TypeLatency::<RequestType>::from_str("query_v2:").is_err());
assert!(TypeLatency::<RequestType>::from_str("query_v2:1gigasecond").is_err());
assert_eq!(
TypeLatency::<RequestType>::from_str("query_v2:100ms").unwrap(),
TypeLatency::<RequestType>(RequestType::QueryV2, Duration::from_millis(100))
);
assert_eq!(
TypeLatency::<RequestType>::from_str("call_v3:1s").unwrap(),
TypeLatency::<RequestType>(RequestType::CallV3, Duration::from_millis(1000))
);
}
#[derive(clap::Parser)]
struct Cli {
#[command(flatten)]
sharded: ShedShardedCli<RequestType>,
#[command(flatten)]
system: ShedSystemCli,
}
#[test]
fn test_cli() {
let args: Vec<&str> = vec![];
Cli::parse_from(args);
}
}