From f79f0c7af22303f311765688b35891738164bd40 Mon Sep 17 00:00:00 2001 From: Byron Wasti Date: Tue, 23 Jul 2024 14:19:16 -0400 Subject: [PATCH] Add more hints Add hints for the starting TPS and the Latency Controller. --- Justfile | 1 - balter-core/src/config.rs | 4 +++ ..._tests__scenario_config_serialization.snap | 4 ++- balter/src/controllers.rs | 23 +++++++++++---- balter/src/controllers/latency.rs | 8 ++++-- balter/src/hints.rs | 7 +++++ balter/src/scenario.rs | 7 +++++ docs/content/guide/_index.md | 28 +++++++++++++++++-- 8 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Justfile b/Justfile index 07c601c..1bb4386 100644 --- a/Justfile +++ b/Justfile @@ -5,7 +5,6 @@ prep: cargo fmt --all -- --check cargo clippy --all-targets -- -D warnings cargo test --release - cargo semver-checks version EXECUTE='' VERSION='minor': cargo release version -p balter -p balter-macros -p balter-core -p balter-runtime {{VERSION}} {{EXECUTE}} diff --git a/balter-core/src/config.rs b/balter-core/src/config.rs index f640ad3..89321c6 100644 --- a/balter-core/src/config.rs +++ b/balter-core/src/config.rs @@ -95,12 +95,16 @@ impl LatencyConfig { #[cfg_attr(feature = "rt", derive(Serialize, Deserialize))] pub struct HintConfig { pub concurrency: usize, + pub starting_tps: Option, + pub latency_controller: Option, } impl Default for HintConfig { fn default() -> Self { Self { concurrency: crate::BASE_CONCURRENCY, + starting_tps: None, + latency_controller: None, } } } diff --git a/balter-core/src/snapshots/balter_core__config__tests__scenario_config_serialization.snap b/balter-core/src/snapshots/balter_core__config__tests__scenario_config_serialization.snap index ae2acc7..d86c349 100644 --- a/balter-core/src/snapshots/balter_core__config__tests__scenario_config_serialization.snap +++ b/balter-core/src/snapshots/balter_core__config__tests__scenario_config_serialization.snap @@ -13,6 +13,8 @@ expression: "ScenarioConfig {\n name: \"test_scenario\".to_string(),\n dur "quantile": 0.99 }, "hints": { - "concurrency": 10 + "concurrency": 10, + "starting_tps": null, + "latency_controller": null } } diff --git a/balter/src/controllers.rs b/balter/src/controllers.rs index 6abaf14..02e120c 100644 --- a/balter/src/controllers.rs +++ b/balter/src/controllers.rs @@ -17,6 +17,7 @@ pub(crate) trait Controller: Send { pub(crate) struct CompositeController { controllers: Vec>, + starting_tps: Option, } impl CompositeController { @@ -36,20 +37,30 @@ impl CompositeController { &config.name, latency, quantile, + config.hints.latency_controller, ))); } - Self { controllers } + let starting_tps = config.hints.starting_tps; + + Self { + controllers, + starting_tps, + } } } impl Controller for CompositeController { fn initial_tps(&self) -> NonZeroU32 { - self.controllers - .iter() - .map(|c| c.initial_tps()) - .min() - .expect("No controllers present.") + if let Some(tps) = self.starting_tps { + tps + } else { + self.controllers + .iter() + .map(|c| c.initial_tps()) + .min() + .expect("No controllers present.") + } } fn limit(&mut self, sample: &Measurement, stable: bool) -> NonZeroU32 { diff --git a/balter/src/controllers/latency.rs b/balter/src/controllers/latency.rs index 89b869c..66da414 100644 --- a/balter/src/controllers/latency.rs +++ b/balter/src/controllers/latency.rs @@ -6,7 +6,7 @@ use std::time::Duration; #[allow(unused)] use tracing::{debug, error, trace}; -const KP: f64 = 0.9; +const DEFAULT_KP: f64 = 0.9; #[allow(unused)] pub(crate) struct LatencyController { @@ -14,15 +14,17 @@ pub(crate) struct LatencyController { latency: Duration, quantile: f64, goal_tps: NonZeroU32, + kp: f64, } impl LatencyController { - pub fn new(name: &str, latency: Duration, quantile: f64) -> Self { + pub fn new(name: &str, latency: Duration, quantile: f64, kp: Option) -> Self { let s = Self { base_label: format!("balter_{name}"), latency, quantile, goal_tps: BASE_TPS, + kp: kp.unwrap_or(DEFAULT_KP), }; s.goal_tps_metric(); s @@ -49,7 +51,7 @@ impl Controller for LatencyController { let normalized_err = 1. - measured_latency.as_secs_f64() / self.latency.as_secs_f64(); trace!("LATENCY: Error {normalized_err:?}"); - let new_goal = self.goal_tps.get() as f64 * (1. + KP * normalized_err); + let new_goal = self.goal_tps.get() as f64 * (1. + self.kp * normalized_err); trace!("LATENCY: New Goal {new_goal:?}"); if let Some(new_goal) = NonZeroU32::new(new_goal as u32) { diff --git a/balter/src/hints.rs b/balter/src/hints.rs index 0920eb0..a6721ff 100644 --- a/balter/src/hints.rs +++ b/balter/src/hints.rs @@ -6,4 +6,11 @@ pub enum Hint { /// Provide the starting concurrency value. Useful for Scenarios with low TPS (which Balter can /// take a long time to stablize on). Concurrency(usize), + + /// Starting TPS for Balter to use. + Tps(u32), + + /// Kp value for the Latency Controller proportional control loop + /// Defaults to 0.9 + LatencyController(f64), } diff --git a/balter/src/scenario.rs b/balter/src/scenario.rs index 6ae04a7..a493fc0 100644 --- a/balter/src/scenario.rs +++ b/balter/src/scenario.rs @@ -208,6 +208,13 @@ where Hint::Concurrency(concurrency) => { self.config.hints.concurrency = concurrency; } + Hint::Tps(tps) => { + self.config.hints.starting_tps = + Some(NonZeroU32::new(tps).expect("TPS hint must be non-zero u32")); + } + Hint::LatencyController(kp) => { + self.config.hints.latency_controller = Some(kp); + } } self } diff --git a/docs/content/guide/_index.md b/docs/content/guide/_index.md index 52d918d..cc2aef6 100644 --- a/docs/content/guide/_index.md +++ b/docs/content/guide/_index.md @@ -128,9 +128,11 @@ test_scaling_functionality() .await; ``` -### Hints +## Hints -For certain Scenarios it can be useful to provide hints for how Balter should run them. This is primarily useful for speeding up the control loops that Balter uses internally, which are designed to work for a wide variety of use-cases and can sometimes be slow. Currently Balter provides one kind of hint, the `Hint::Concurrency` which Balter will use as the starting concurrency for a given Scenario: +For certain Scenarios it can be useful to provide hints for how Balter should run them. This is primarily useful for speeding up the control loops that Balter uses internally, which are designed to work for a wide variety of use-cases and can sometimes be slow. Currently Balter provides only a few hints. + +- `Hint::Concurrency` which Balter will use as the starting concurrency for a given Scenario: ```rust use balter::{prelude::*, Hint}; @@ -141,6 +143,28 @@ my_scenario() .await; ``` +- `Hint::Tps` which Balter will use as the starting TPS for a given Scenario: + +```rust +use balter::{prelude::*, Hint}; + +my_scenario() + .tps(10_000) + .hint(Hint::Tps(10_000)) + .await; +``` + +- `Hint::LatencyController` which sets the `Kp` value used by the proportional control loop in the Latency Controller. Use to speed up convergence, though beware of instability. Defaults to `0.9`. + +```rust +use balter::{prelude::*, Hint}; + +my_scenario() + .tps(10_000) + .hint(Hint::Latency(1.2)) + .await; +``` + ## Statistics Scenario's will return statistical information about the run. For example,