From 1f49368146ca049698d7bfac037f2f6a1b3d62b2 Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Sat, 9 Oct 2021 08:08:55 +0200 Subject: [PATCH 01/10] Revert "Improve naming and comments" This reverts commit 8612e6eda9a8df2eb65f25141dd97d8d41d2689b. --- src/user.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/user.rs b/src/user.rs index db4e4ff4..a4bb5f71 100644 --- a/src/user.rs +++ b/src/user.rs @@ -53,8 +53,8 @@ pub(crate) async fn user_main( // If normal tasks are defined, loop launching tasks until parent tells us to stop. if !thread_task_set.weighted_tasks.is_empty() { let mut task_iter = thread_task_set.weighted_tasks.iter().cycle(); - let next_task_delay = Fuse::terminated(); - pin_mut!(next_task_delay); + let next_task = Fuse::terminated(); + pin_mut!(next_task); let task_wait = match thread_task_set.task_wait.take() { Some((min, max)) if min == max => min, @@ -64,10 +64,10 @@ pub(crate) async fn user_main( None => Duration::from_millis(0), }; - next_task_delay.set(tokio::time::sleep(Duration::from_secs(0)).fuse()); + next_task.set(tokio::time::sleep(Duration::from_secs(0)).fuse()); loop { select! { - _ = next_task_delay => { + _ = next_task => { let (thread_task_index, thread_task_name) = task_iter.next().unwrap(); if *thread_task_index == 0 { // Tracks the time it takes to loop through all GooseTasks when Coordinated Omission @@ -75,8 +75,9 @@ pub(crate) async fn user_main( thread_user.update_request_cadence(thread_number).await; } - // Get a reference to the task function we're going to invoke next. - let function = &thread_task_set.tasks[*thread_task_index].function; + // Determine which task we're going to run next. + let task = &thread_task_set.tasks[*thread_task_index]; + let function = &task.function; debug!( "launching on_start {} task from {}", thread_task_name, thread_task_set.name @@ -95,9 +96,9 @@ pub(crate) async fn user_main( let elapsed = now.elapsed(); if elapsed < task_wait { - next_task_delay.set(tokio::time::sleep(task_wait - elapsed).fuse()); + next_task.set(tokio::time::sleep(task_wait - elapsed).fuse()); } else { - next_task_delay.set(tokio::time::sleep(Duration::from_millis(0)).fuse()); + next_task.set(tokio::time::sleep(Duration::from_millis(0)).fuse()); } }, message = thread_receiver.recv_async().fuse() => { From e3ccdd701bc8e7e982eb9d86d9345e88c97b4cab Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Sat, 9 Oct 2021 08:10:25 +0200 Subject: [PATCH 02/10] Revert "Allow to set duration instead of seconds for task wait configuration" This reverts commit ef711076f16cf2f4e4d492c68a087dc64250eff3. --- examples/simple.rs | 4 +- examples/simple_closure.rs | 3 +- examples/simple_with_session.rs | 4 +- examples/umami/main.rs | 8 +- src/goose.rs | 111 ++++++++++++++++------------ src/lib.rs | 9 ++- src/manager.rs | 6 ++ src/user.rs | 125 +++++++++++++++++--------------- src/worker.rs | 2 + 9 files changed, 148 insertions(+), 124 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index a9b2e56f..ba4c0a47 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -17,8 +17,6 @@ //! See the License for the specific language governing permissions and //! limitations under the License. -use std::time::Duration; - use goose::prelude::*; #[tokio::main] @@ -28,7 +26,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))? + .set_wait_time(5, 15)? // This task only runs one time when the user first starts. .register_task(task!(website_login).set_on_start()) // These next two tasks run repeatedly as long as the load test is running. diff --git a/examples/simple_closure.rs b/examples/simple_closure.rs index 9c106c2e..b077c267 100644 --- a/examples/simple_closure.rs +++ b/examples/simple_closure.rs @@ -20,13 +20,12 @@ use goose::prelude::*; use std::boxed::Box; use std::sync::Arc; -use std::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { let mut taskset = taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))?; + .set_wait_time(5, 15)?; let paths = vec!["/", "/about", "/our-team"]; for request_path in paths { diff --git a/examples/simple_with_session.rs b/examples/simple_with_session.rs index 23537196..923f1ffb 100644 --- a/examples/simple_with_session.rs +++ b/examples/simple_with_session.rs @@ -17,8 +17,6 @@ //! See the License for the specific language governing permissions and //! limitations under the License. -use std::time::Duration; - use goose::prelude::*; use serde::Deserialize; @@ -39,7 +37,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))? + .set_wait_time(5, 15)? // This task only runs one time when the user first starts. .register_task(task!(website_signup).set_on_start()) // These next two tasks run repeatedly as long as the load test is running. diff --git a/examples/umami/main.rs b/examples/umami/main.rs index 9266285a..9842854d 100644 --- a/examples/umami/main.rs +++ b/examples/umami/main.rs @@ -3,8 +3,6 @@ mod common; mod english; mod spanish; -use std::time::Duration; - use goose::prelude::*; use crate::admin::*; @@ -20,7 +18,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Anonymous English user") .set_weight(40)? - .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? + .set_wait_time(0, 3)? .register_task(task!(front_page_en).set_name("anon /").set_weight(2)?) .register_task(task!(basic_page_en).set_name("anon /en/basicpage")) .register_task(task!(article_listing_en).set_name("anon /en/articles/")) @@ -47,7 +45,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Anonymous Spanish user") .set_weight(9)? - .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? + .set_wait_time(0, 3)? .register_task(task!(front_page_es).set_name("anon /es/").set_weight(2)?) .register_task(task!(basic_page_es).set_name("anon /es/basicpage")) .register_task(task!(article_listing_es).set_name("anon /es/articles/")) @@ -73,7 +71,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Admin user") .set_weight(1)? - .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? + .set_wait_time(3, 10)? .register_task(task!(log_in).set_on_start().set_name("auth /en/user/login")) .register_task(task!(front_page_en).set_name("auth /").set_weight(2)?) .register_task(task!(article_listing_en).set_name("auth /en/articles/")) diff --git a/src/goose.rs b/src/goose.rs index 9e936c1e..41425773 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -55,18 +55,17 @@ //! //! ### Task Set Wait Time //! -//! Wait time is specified as a low-high duration range. Each time a task completes in -//! the task set, the user will pause for a random number of milliseconds inclusively between +//! Wait time is specified as a low-high integer range. Each time a task completes in +//! the task set, the user will pause for a random number of seconds inclusively between //! the low and high wait times. In the following example, users loading `foo` tasks will //! sleep 0 to 3 seconds after each task completes, and users loading `bar` tasks will //! sleep 5 to 10 seconds after each task completes. //! //! ```rust //! use goose::prelude::*; -//! use std::time::Duration; //! -//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(3)).unwrap(); -//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap(); +//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(0, 3).unwrap(); +//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(5, 10).unwrap(); //! ``` //! ## Creating Tasks //! @@ -293,7 +292,6 @@ use reqwest::{header, Client, ClientBuilder, RequestBuilder, Response}; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use std::time::Duration; use std::{fmt, str}; use std::{future::Future, pin::Pin, time::Instant}; use tokio::sync::RwLock; @@ -472,8 +470,10 @@ pub struct GooseTaskSet { pub task_sets_index: usize, /// An integer value that controls the frequency that this task set will be assigned to a user. pub weight: usize, - /// An range of duration indicating the interval a user will sleep after running a task. - pub task_wait: Option<(Duration, Duration)>, + /// An integer value indicating the minimum number of seconds a user will sleep after running a task. + pub min_wait: usize, + /// An integer value indicating the maximum number of seconds a user will sleep after running a task. + pub max_wait: usize, /// A vector containing one copy of each [`GooseTask`](./struct.GooseTask.html) that will /// run by users running this task set. pub tasks: Vec, @@ -509,7 +509,8 @@ impl GooseTaskSet { name: name.to_string(), task_sets_index: usize::max_value(), weight: 1, - task_wait: None, + min_wait: 0, + max_wait: 0, tasks: Vec::new(), weighted_tasks: Vec::new(), weighted_on_start_tasks: Vec::new(), @@ -591,32 +592,27 @@ impl GooseTaskSet { self } - /// Configure a duration per task_set to pause after running each task. The length of the pause will be randomly - /// selected from `min_wait` to `max_wait` inclusively. For example, if `min_wait` is `Duration::from_secs(0)` and - /// `max_wait` is `Duration::from_secs(2)`, the user will randomly sleep between 0 and 2_000 milliseconds after each task completes. + /// Configure a task_set to to pause after running each task. The length of the pause will be randomly + /// selected from `min_weight` to `max_wait` inclusively. For example, if `min_wait` is `0` and + /// `max_weight` is `2`, the user will randomly sleep for 0, 1 or 2 seconds after each task completes. /// /// # Example /// ```rust /// use goose::prelude::*; - /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { - /// taskset!("ExampleTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(1))?; + /// taskset!("ExampleTasks").set_wait_time(0, 1)?; /// /// Ok(()) /// } /// ``` - pub fn set_wait_time( - mut self, - min_wait: Duration, - max_wait: Duration, - ) -> Result { + pub fn set_wait_time(mut self, min_wait: usize, max_wait: usize) -> Result { trace!( - "{} set_wait time: min: {}ms max: {}ms", + "{} set_wait time: min: {} max: {}", self.name, - min_wait.as_millis(), - max_wait.as_millis() + min_wait, + max_wait ); if min_wait > max_wait { return Err(GooseError::InvalidWaitTime { @@ -627,7 +623,8 @@ impl GooseTaskSet { .to_string(), }); } - self.task_wait.replace((min_wait, max_wait)); + self.min_wait = min_wait; + self.max_wait = max_wait; Ok(self) } @@ -741,6 +738,10 @@ pub struct GaggleUser { pub task_sets_index: usize, /// The base URL to prepend to all relative paths. pub base_url: Arc>, + /// Minimum amount of time to sleep after running a task. + pub min_wait: usize, + /// Maximum amount of time to sleep after running a task. + pub max_wait: usize, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// Load test hash. @@ -751,6 +752,8 @@ impl GaggleUser { pub fn new( task_sets_index: usize, base_url: Url, + min_wait: usize, + max_wait: usize, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Self { @@ -758,6 +761,8 @@ impl GaggleUser { GaggleUser { task_sets_index, base_url: Arc::new(RwLock::new(base_url)), + min_wait, + max_wait, config: configuration.clone(), load_test_hash, } @@ -842,6 +847,10 @@ pub struct GooseUser { pub client: Client, /// The base URL to prepend to all relative paths. pub base_url: Url, + /// Minimum amount of time to sleep after running a task. + pub min_wait: usize, + /// Maximum amount of time to sleep after running a task. + pub max_wait: usize, /// A local copy of the global [`GooseConfiguration`](../struct.GooseConfiguration.html). pub config: GooseConfiguration, /// Channel to logger. @@ -875,6 +884,8 @@ impl GooseUser { pub fn new( task_sets_index: usize, base_url: Url, + min_wait: usize, + max_wait: usize, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Result { @@ -891,6 +902,8 @@ impl GooseUser { task_sets_index, client, base_url, + min_wait, + max_wait, config: configuration.clone(), logger: None, throttle: None, @@ -908,7 +921,7 @@ impl GooseUser { /// Create a new single-use user. pub fn single(base_url: Url, configuration: &GooseConfiguration) -> Result { - let mut single_user = GooseUser::new(0, base_url, configuration, 0)?; + let mut single_user = GooseUser::new(0, base_url, 0, 0, configuration, 0)?; // Only one user, so index is 0. single_user.weighted_users_index = 0; // Do not throttle [`test_start`](../struct.GooseAttack.html#method.test_start) (setup) and @@ -2245,14 +2258,13 @@ impl GooseUser { /// # Example /// ```rust /// use goose::prelude::*; - /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { /// let _goose_metrics = GooseAttack::initialize()? /// .register_taskset(taskset!("LoadtestTasks") /// .set_host("http://foo.example.com/") - /// .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? + /// .set_wait_time(0, 3)? /// .register_task(task!(task_foo).set_weight(10)?) /// .register_task(task!(task_bar)) /// ) @@ -2660,7 +2672,8 @@ mod tests { assert_eq!(task_set.name, "foo"); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.task_wait, None); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); assert!(task_set.host.is_none()); assert_eq!(task_set.tasks.len(), 0); assert_eq!(task_set.weighted_tasks.len(), 0); @@ -2673,7 +2686,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.task_wait, None); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); assert!(task_set.host.is_none()); // Different task can be registered. @@ -2682,7 +2696,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.task_wait, None); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); assert!(task_set.host.is_none()); // Same task can be registered again. @@ -2691,7 +2706,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.task_wait, None); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); assert!(task_set.host.is_none()); // Setting weight only affects weight field. @@ -2700,7 +2716,8 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.task_wait, None); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); assert!(task_set.host.is_none()); // Weight can be changed. @@ -2714,19 +2731,17 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); + assert_eq!(task_set.min_wait, 0); + assert_eq!(task_set.max_wait, 0); // Host field can be changed. task_set = task_set.set_host("https://bar.example.com/"); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); // Wait time only affects wait time fields. - task_set = task_set - .set_wait_time(Duration::from_secs(1), Duration::from_secs(10)) - .unwrap(); - assert_eq!( - task_set.task_wait, - Some((Duration::from_secs(1), Duration::from_secs(10))) - ); + task_set = task_set.set_wait_time(1, 10).unwrap(); + assert_eq!(task_set.min_wait, 1); + assert_eq!(task_set.max_wait, 10); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); assert_eq!(task_set.weight, 5); assert_eq!(task_set.tasks.len(), 3); @@ -2734,13 +2749,9 @@ mod tests { assert_eq!(task_set.task_sets_index, usize::max_value()); // Wait time can be changed. - task_set = task_set - .set_wait_time(Duration::from_secs(3), Duration::from_secs(9)) - .unwrap(); - assert_eq!( - task_set.task_wait, - Some((Duration::from_secs(3), Duration::from_secs(9))) - ); + task_set = task_set.set_wait_time(3, 9).unwrap(); + assert_eq!(task_set.min_wait, 3); + assert_eq!(task_set.max_wait, 9); } #[test] @@ -2828,8 +2839,10 @@ mod tests { const HOST: &str = "http://example.com/"; let configuration = GooseConfiguration::parse_args_default(&EMPTY_ARGS).unwrap(); let base_url = get_base_url(Some(HOST.to_string()), None, None).unwrap(); - let user = GooseUser::new(0, base_url, &configuration, 0).unwrap(); + let user = GooseUser::new(0, base_url, 0, 0, &configuration, 0).unwrap(); assert_eq!(user.task_sets_index, 0); + assert_eq!(user.min_wait, 0); + assert_eq!(user.max_wait, 0); assert_eq!(user.weighted_users_index, usize::max_value()); // Confirm the URLs are correctly built using the default_host. @@ -2855,7 +2868,9 @@ mod tests { Some("http://www.example.com/".to_string()), ) .unwrap(); - let user2 = GooseUser::new(0, base_url, &configuration, 0).unwrap(); + let user2 = GooseUser::new(0, base_url, 1, 3, &configuration, 0).unwrap(); + assert_eq!(user2.min_wait, 1); + assert_eq!(user2.max_wait, 3); // Confirm the URLs are correctly built using the task_set_host. let url = user2.build_url("/foo").unwrap(); @@ -2914,7 +2929,7 @@ mod tests { // Confirm Goose can build a base_url that includes a path. const HOST_WITH_PATH: &str = "http://example.com/with/path/"; let base_url = get_base_url(Some(HOST_WITH_PATH.to_string()), None, None).unwrap(); - let user = GooseUser::new(0, base_url, &configuration, 0).unwrap(); + let user = GooseUser::new(0, base_url, 0, 0, &configuration, 0).unwrap(); // Confirm the URLs are correctly built using the default_host that includes a path. let url = user.build_url("foo").unwrap(); diff --git a/src/lib.rs b/src/lib.rs index f2e165aa..f9554deb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -468,7 +468,6 @@ use std::sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }; -use std::time::Duration; use std::{fmt, io, time}; use tokio::fs::File; @@ -551,9 +550,9 @@ pub enum GooseError { /// Invalid wait time specified. InvalidWaitTime { // The specified minimum wait time. - min_wait: Duration, + min_wait: usize, // The specified maximum wait time. - max_wait: Duration, + max_wait: usize, /// An optional explanation of the error. detail: String, }, @@ -1138,6 +1137,8 @@ impl GooseAttack { weighted_users.push(GooseUser::new( self.task_sets[*task_sets_index].task_sets_index, base_url, + self.task_sets[*task_sets_index].min_wait, + self.task_sets[*task_sets_index].max_wait, &self.configuration, self.metrics.hash, )?); @@ -1171,6 +1172,8 @@ impl GooseAttack { weighted_users.push(GaggleUser::new( self.task_sets[*task_sets_index].task_sets_index, base_url, + self.task_sets[*task_sets_index].min_wait, + self.task_sets[*task_sets_index].max_wait, &self.configuration, self.metrics.hash, )); diff --git a/src/manager.rs b/src/manager.rs index a0af5836..1df34149 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -25,6 +25,10 @@ pub struct GooseUserInitializer { pub task_sets_index: usize, /// The base_url for this user thread. pub base_url: String, + /// Minimum amount of time to sleep after running a task. + pub min_wait: usize, + /// Maximum amount of time to sleep after running a task. + pub max_wait: usize, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// How long the load test should run, in seconds. @@ -470,6 +474,8 @@ pub(crate) async fn manager_main(mut goose_attack: GooseAttack) -> GooseAttack { users.push(GooseUserInitializer { task_sets_index: user.task_sets_index, base_url: user.base_url.read().await.to_string(), + min_wait: user.min_wait, + max_wait: user.max_wait, config: user.config.clone(), run_time: goose_attack.run_time, worker_id: workers.len(), diff --git a/src/user.rs b/src/user.rs index a4bb5f71..323475f5 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,7 +1,5 @@ -use futures::future::Fuse; -use futures::{pin_mut, select, FutureExt}; use rand::Rng; -use std::time::{self, Duration, Instant}; +use std::time; use crate::get_worker_id; use crate::goose::{GooseTaskFunction, GooseTaskSet, GooseUser, GooseUserCommand}; @@ -10,7 +8,7 @@ use crate::metrics::{GooseMetric, GooseTaskMetric}; pub(crate) async fn user_main( thread_number: usize, - mut thread_task_set: GooseTaskSet, + thread_task_set: GooseTaskSet, mut thread_user: GooseUser, thread_receiver: flume::Receiver, worker: bool, @@ -52,66 +50,73 @@ pub(crate) async fn user_main( // If normal tasks are defined, loop launching tasks until parent tells us to stop. if !thread_task_set.weighted_tasks.is_empty() { - let mut task_iter = thread_task_set.weighted_tasks.iter().cycle(); - let next_task = Fuse::terminated(); - pin_mut!(next_task); - - let task_wait = match thread_task_set.task_wait.take() { - Some((min, max)) if min == max => min, - Some((min, max)) => Duration::from_millis( - rand::thread_rng().gen_range(min.as_millis()..max.as_millis()) as u64, - ), - None => Duration::from_millis(0), - }; - - next_task.set(tokio::time::sleep(Duration::from_secs(0)).fuse()); - loop { - select! { - _ = next_task => { - let (thread_task_index, thread_task_name) = task_iter.next().unwrap(); - if *thread_task_index == 0 { - // Tracks the time it takes to loop through all GooseTasks when Coordinated Omission - // Mitigation is enabled. - thread_user.update_request_cadence(thread_number).await; - } - - // Determine which task we're going to run next. - let task = &thread_task_set.tasks[*thread_task_index]; - let function = &task.function; - debug!( - "launching on_start {} task from {}", - thread_task_name, thread_task_set.name - ); - - let now = Instant::now(); - // Invoke the task function. - let _ = invoke_task_function( - function, - &mut thread_user, - *thread_task_index, - thread_task_name, - ) - .await; - - let elapsed = now.elapsed(); - - if elapsed < task_wait { - next_task.set(tokio::time::sleep(task_wait - elapsed).fuse()); - } else { - next_task.set(tokio::time::sleep(Duration::from_millis(0)).fuse()); - } - }, - message = thread_receiver.recv_async().fuse() => { - match message { - // Time to exit, break out of launch_tasks loop. - Err(_) | Ok(GooseUserCommand::Exit) => { - break ; + 'launch_tasks: loop { + // Tracks the time it takes to loop through all GooseTasks when Coordinated Omission + // Mitigation is enabled. + thread_user.update_request_cadence(thread_number).await; + + for (thread_task_index, thread_task_name) in &thread_task_set.weighted_tasks { + // Determine which task we're going to run next. + let function = &thread_task_set.tasks[*thread_task_index].function; + debug!( + "launching on_start {} task from {}", + thread_task_name, thread_task_set.name + ); + // Invoke the task function. + let _todo = invoke_task_function( + function, + &mut thread_user, + *thread_task_index, + thread_task_name, + ) + .await; + + // Prepare to sleep for a random value from min_wait to max_wait. + let wait_time = if thread_user.max_wait > 0 { + rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) + } else { + 0 + }; + + // Counter to track how long we've slept, waking regularly to check for messages. + let mut slept: usize = 0; + + // Wake every second to check if the parent thread has told us to exit. + let mut in_sleep_loop = true; + // Track the time slept for Coordinated Omission Mitigation. + let sleep_timer = time::Instant::now(); + while in_sleep_loop { + let mut message = thread_receiver.try_recv(); + while message.is_ok() { + match message.unwrap() { + // Time to exit, break out of launch_tasks loop. + GooseUserCommand::Exit => { + break 'launch_tasks; + } + command => { + debug!("ignoring unexpected GooseUserCommand: {:?}", command); + } } - Ok(command) => { - debug!("ignoring unexpected GooseUserCommand: {:?}", command); + message = thread_receiver.try_recv(); + } + if thread_user.max_wait > 0 { + let sleep_duration = time::Duration::from_secs(1); + debug!( + "user {} from {} sleeping {:?} second...", + thread_number, thread_task_set.name, sleep_duration + ); + tokio::time::sleep(sleep_duration).await; + slept += 1; + if slept > wait_time { + in_sleep_loop = false; } + } else { + in_sleep_loop = false; } } + // Track how much time the GooseUser sleeps during this loop through all GooseTasks, + // used by Coordinated Omission Mitigation. + thread_user.slept += (time::Instant::now() - sleep_timer).as_millis() as u64; } } } diff --git a/src/worker.rs b/src/worker.rs index ded9060d..db30449b 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -139,6 +139,8 @@ pub(crate) async fn worker_main(goose_attack: GooseAttack) -> GooseAttack { let user = GooseUser::new( initializer.task_sets_index, Url::parse(&initializer.base_url).unwrap(), + initializer.min_wait, + initializer.max_wait, &initializer.config, goose_attack.metrics.hash, ) From 7f158d30177b01bb0b16178e204e08f2c3d4ee8c Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Sun, 10 Oct 2021 08:20:37 +0200 Subject: [PATCH 03/10] convert min_wait and max_wait to Duration --- examples/simple.rs | 3 +- examples/simple_closure.rs | 4 +- examples/simple_with_session.rs | 3 +- examples/umami/main.rs | 7 +- src/goose.rs | 142 +++++++++++++++++++++----------- src/lib.rs | 4 +- src/manager.rs | 4 +- src/user.rs | 22 +++-- 8 files changed, 124 insertions(+), 65 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index ba4c0a47..09f86581 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -18,6 +18,7 @@ //! limitations under the License. use goose::prelude::*; +use std::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { @@ -26,7 +27,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(5, 15)? + .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))? // This task only runs one time when the user first starts. .register_task(task!(website_login).set_on_start()) // These next two tasks run repeatedly as long as the load test is running. diff --git a/examples/simple_closure.rs b/examples/simple_closure.rs index b077c267..3519ba5e 100644 --- a/examples/simple_closure.rs +++ b/examples/simple_closure.rs @@ -17,15 +17,15 @@ //! limitations under the License. use goose::prelude::*; - use std::boxed::Box; use std::sync::Arc; +use std::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { let mut taskset = taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(5, 15)?; + .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))?; let paths = vec!["/", "/about", "/our-team"]; for request_path in paths { diff --git a/examples/simple_with_session.rs b/examples/simple_with_session.rs index 923f1ffb..e077e42a 100644 --- a/examples/simple_with_session.rs +++ b/examples/simple_with_session.rs @@ -19,6 +19,7 @@ use goose::prelude::*; use serde::Deserialize; +use std::time::Duration; struct Session { jwt_token: String, @@ -37,7 +38,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("WebsiteUser") // After each task runs, sleep randomly from 5 to 15 seconds. - .set_wait_time(5, 15)? + .set_wait_time(Duration::from_secs(5), Duration::from_secs(15))? // This task only runs one time when the user first starts. .register_task(task!(website_signup).set_on_start()) // These next two tasks run repeatedly as long as the load test is running. diff --git a/examples/umami/main.rs b/examples/umami/main.rs index 9842854d..4b0789a2 100644 --- a/examples/umami/main.rs +++ b/examples/umami/main.rs @@ -4,6 +4,7 @@ mod english; mod spanish; use goose::prelude::*; +use std::time::Duration; use crate::admin::*; use crate::english::*; @@ -18,7 +19,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Anonymous English user") .set_weight(40)? - .set_wait_time(0, 3)? + .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? .register_task(task!(front_page_en).set_name("anon /").set_weight(2)?) .register_task(task!(basic_page_en).set_name("anon /en/basicpage")) .register_task(task!(article_listing_en).set_name("anon /en/articles/")) @@ -45,7 +46,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Anonymous Spanish user") .set_weight(9)? - .set_wait_time(0, 3)? + .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? .register_task(task!(front_page_es).set_name("anon /es/").set_weight(2)?) .register_task(task!(basic_page_es).set_name("anon /es/basicpage")) .register_task(task!(article_listing_es).set_name("anon /es/articles/")) @@ -71,7 +72,7 @@ async fn main() -> Result<(), GooseError> { .register_taskset( taskset!("Admin user") .set_weight(1)? - .set_wait_time(3, 10)? + .set_wait_time(Duration::from_secs(3), Duration::from_secs(10))? .register_task(task!(log_in).set_on_start().set_name("auth /en/user/login")) .register_task(task!(front_page_en).set_name("auth /").set_weight(2)?) .register_task(task!(article_listing_en).set_name("auth /en/articles/")) diff --git a/src/goose.rs b/src/goose.rs index 41425773..8269c37d 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -63,9 +63,10 @@ //! //! ```rust //! use goose::prelude::*; +//! use std::time::Duration; //! -//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(0, 3).unwrap(); -//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(5, 10).unwrap(); +//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(3)).unwrap(); +//! let mut bar_tasks = taskset!("BarTasks").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap(); //! ``` //! ## Creating Tasks //! @@ -470,10 +471,12 @@ pub struct GooseTaskSet { pub task_sets_index: usize, /// An integer value that controls the frequency that this task set will be assigned to a user. pub weight: usize, - /// An integer value indicating the minimum number of seconds a user will sleep after running a task. - pub min_wait: usize, - /// An integer value indicating the maximum number of seconds a user will sleep after running a task. - pub max_wait: usize, + /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the + /// minimum time a [`GooseUser`] will sleep after running a task. + pub min_wait: std::time::Duration, + /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the + /// maximum time a [`GooseUser`] will sleep after running a task. + pub max_wait: std::time::Duration, /// A vector containing one copy of each [`GooseTask`](./struct.GooseTask.html) that will /// run by users running this task set. pub tasks: Vec, @@ -509,8 +512,8 @@ impl GooseTaskSet { name: name.to_string(), task_sets_index: usize::max_value(), weight: 1, - min_wait: 0, - max_wait: 0, + min_wait: std::time::Duration::from_secs(0), + max_wait: std::time::Duration::from_secs(0), tasks: Vec::new(), weighted_tasks: Vec::new(), weighted_on_start_tasks: Vec::new(), @@ -599,17 +602,22 @@ impl GooseTaskSet { /// # Example /// ```rust /// use goose::prelude::*; + /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { - /// taskset!("ExampleTasks").set_wait_time(0, 1)?; + /// taskset!("ExampleTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(1))?; /// /// Ok(()) /// } /// ``` - pub fn set_wait_time(mut self, min_wait: usize, max_wait: usize) -> Result { + pub fn set_wait_time( + mut self, + min_wait: std::time::Duration, + max_wait: std::time::Duration, + ) -> Result { trace!( - "{} set_wait time: min: {} max: {}", + "{} set_wait time: min: {:?} max: {:?}", self.name, min_wait, max_wait @@ -739,9 +747,9 @@ pub struct GaggleUser { /// The base URL to prepend to all relative paths. pub base_url: Arc>, /// Minimum amount of time to sleep after running a task. - pub min_wait: usize, + pub min_wait: std::time::Duration, /// Maximum amount of time to sleep after running a task. - pub max_wait: usize, + pub max_wait: std::time::Duration, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// Load test hash. @@ -752,8 +760,8 @@ impl GaggleUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: usize, - max_wait: usize, + min_wait: std::time::Duration, + max_wait: std::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Self { @@ -848,9 +856,9 @@ pub struct GooseUser { /// The base URL to prepend to all relative paths. pub base_url: Url, /// Minimum amount of time to sleep after running a task. - pub min_wait: usize, + pub min_wait: std::time::Duration, /// Maximum amount of time to sleep after running a task. - pub max_wait: usize, + pub max_wait: std::time::Duration, /// A local copy of the global [`GooseConfiguration`](../struct.GooseConfiguration.html). pub config: GooseConfiguration, /// Channel to logger. @@ -884,8 +892,8 @@ impl GooseUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: usize, - max_wait: usize, + min_wait: std::time::Duration, + max_wait: std::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Result { @@ -921,7 +929,14 @@ impl GooseUser { /// Create a new single-use user. pub fn single(base_url: Url, configuration: &GooseConfiguration) -> Result { - let mut single_user = GooseUser::new(0, base_url, 0, 0, configuration, 0)?; + let mut single_user = GooseUser::new( + 0, + base_url, + std::time::Duration::from_secs(0), + std::time::Duration::from_secs(0), + configuration, + 0, + )?; // Only one user, so index is 0. single_user.weighted_users_index = 0; // Do not throttle [`test_start`](../struct.GooseAttack.html#method.test_start) (setup) and @@ -2258,13 +2273,14 @@ impl GooseUser { /// # Example /// ```rust /// use goose::prelude::*; + /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { /// let _goose_metrics = GooseAttack::initialize()? /// .register_taskset(taskset!("LoadtestTasks") /// .set_host("http://foo.example.com/") - /// .set_wait_time(0, 3)? + /// .set_wait_time(Duration::from_secs(0), Duration::from_secs(3))? /// .register_task(task!(task_foo).set_weight(10)?) /// .register_task(task!(task_bar)) /// ) @@ -2672,8 +2688,8 @@ mod tests { assert_eq!(task_set.name, "foo"); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); assert_eq!(task_set.tasks.len(), 0); assert_eq!(task_set.weighted_tasks.len(), 0); @@ -2686,8 +2702,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Different task can be registered. @@ -2696,8 +2712,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Same task can be registered again. @@ -2706,8 +2722,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Setting weight only affects weight field. @@ -2716,8 +2732,8 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Weight can be changed. @@ -2731,17 +2747,22 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, 0); - assert_eq!(task_set.max_wait, 0); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); // Host field can be changed. task_set = task_set.set_host("https://bar.example.com/"); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); // Wait time only affects wait time fields. - task_set = task_set.set_wait_time(1, 10).unwrap(); - assert_eq!(task_set.min_wait, 1); - assert_eq!(task_set.max_wait, 10); + task_set = task_set + .set_wait_time( + std::time::Duration::from_secs(1), + std::time::Duration::from_secs(10), + ) + .unwrap(); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(1)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(10)); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); assert_eq!(task_set.weight, 5); assert_eq!(task_set.tasks.len(), 3); @@ -2749,9 +2770,14 @@ mod tests { assert_eq!(task_set.task_sets_index, usize::max_value()); // Wait time can be changed. - task_set = task_set.set_wait_time(3, 9).unwrap(); - assert_eq!(task_set.min_wait, 3); - assert_eq!(task_set.max_wait, 9); + task_set = task_set + .set_wait_time( + std::time::Duration::from_secs(3), + std::time::Duration::from_secs(9), + ) + .unwrap(); + assert_eq!(task_set.min_wait, std::time::Duration::from_secs(3)); + assert_eq!(task_set.max_wait, std::time::Duration::from_secs(9)); } #[test] @@ -2839,10 +2865,18 @@ mod tests { const HOST: &str = "http://example.com/"; let configuration = GooseConfiguration::parse_args_default(&EMPTY_ARGS).unwrap(); let base_url = get_base_url(Some(HOST.to_string()), None, None).unwrap(); - let user = GooseUser::new(0, base_url, 0, 0, &configuration, 0).unwrap(); + let user = GooseUser::new( + 0, + base_url, + std::time::Duration::from_secs(0), + std::time::Duration::from_secs(0), + &configuration, + 0, + ) + .unwrap(); assert_eq!(user.task_sets_index, 0); - assert_eq!(user.min_wait, 0); - assert_eq!(user.max_wait, 0); + assert_eq!(user.min_wait, std::time::Duration::from_secs(0)); + assert_eq!(user.max_wait, std::time::Duration::from_secs(0)); assert_eq!(user.weighted_users_index, usize::max_value()); // Confirm the URLs are correctly built using the default_host. @@ -2868,9 +2902,17 @@ mod tests { Some("http://www.example.com/".to_string()), ) .unwrap(); - let user2 = GooseUser::new(0, base_url, 1, 3, &configuration, 0).unwrap(); - assert_eq!(user2.min_wait, 1); - assert_eq!(user2.max_wait, 3); + let user2 = GooseUser::new( + 0, + base_url, + std::time::Duration::from_secs(1), + std::time::Duration::from_secs(3), + &configuration, + 0, + ) + .unwrap(); + assert_eq!(user2.min_wait, std::time::Duration::from_secs(1)); + assert_eq!(user2.max_wait, std::time::Duration::from_secs(3)); // Confirm the URLs are correctly built using the task_set_host. let url = user2.build_url("/foo").unwrap(); @@ -2929,7 +2971,15 @@ mod tests { // Confirm Goose can build a base_url that includes a path. const HOST_WITH_PATH: &str = "http://example.com/with/path/"; let base_url = get_base_url(Some(HOST_WITH_PATH.to_string()), None, None).unwrap(); - let user = GooseUser::new(0, base_url, 0, 0, &configuration, 0).unwrap(); + let user = GooseUser::new( + 0, + base_url, + std::time::Duration::from_secs(0), + std::time::Duration::from_secs(0), + &configuration, + 0, + ) + .unwrap(); // Confirm the URLs are correctly built using the default_host that includes a path. let url = user.build_url("foo").unwrap(); diff --git a/src/lib.rs b/src/lib.rs index f9554deb..8ae8f5a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -550,9 +550,9 @@ pub enum GooseError { /// Invalid wait time specified. InvalidWaitTime { // The specified minimum wait time. - min_wait: usize, + min_wait: std::time::Duration, // The specified maximum wait time. - max_wait: usize, + max_wait: std::time::Duration, /// An optional explanation of the error. detail: String, }, diff --git a/src/manager.rs b/src/manager.rs index 1df34149..775a1bbd 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -26,9 +26,9 @@ pub struct GooseUserInitializer { /// The base_url for this user thread. pub base_url: String, /// Minimum amount of time to sleep after running a task. - pub min_wait: usize, + pub min_wait: std::time::Duration, /// Maximum amount of time to sleep after running a task. - pub max_wait: usize, + pub max_wait: std::time::Duration, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// How long the load test should run, in seconds. diff --git a/src/user.rs b/src/user.rs index 323475f5..e4374c08 100644 --- a/src/user.rs +++ b/src/user.rs @@ -72,14 +72,14 @@ pub(crate) async fn user_main( .await; // Prepare to sleep for a random value from min_wait to max_wait. - let wait_time = if thread_user.max_wait > 0 { + let wait_time = if thread_user.max_wait.as_millis() > 0 { rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) } else { - 0 + std::time::Duration::from_secs(0) }; // Counter to track how long we've slept, waking regularly to check for messages. - let mut slept: usize = 0; + let mut slept: u128 = 0; // Wake every second to check if the parent thread has told us to exit. let mut in_sleep_loop = true; @@ -99,15 +99,21 @@ pub(crate) async fn user_main( } message = thread_receiver.try_recv(); } - if thread_user.max_wait > 0 { - let sleep_duration = time::Duration::from_secs(1); + if thread_user.max_wait.as_millis() > 0 { + let sleep_duration = if thread_user.max_wait.as_secs() >= 1 { + slept += 1000; + std::time::Duration::from_secs(1) + } else { + slept += thread_user.max_wait.as_millis(); + thread_user.max_wait + }; + debug!( - "user {} from {} sleeping {:?} second...", + "user {} from {} sleeping {:?} ...", thread_number, thread_task_set.name, sleep_duration ); tokio::time::sleep(sleep_duration).await; - slept += 1; - if slept > wait_time { + if slept > wait_time.as_millis() { in_sleep_loop = false; } } else { From a32d8632c3740c66b42188aa1feef441b3ec6864 Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Sun, 10 Oct 2021 09:43:39 +0200 Subject: [PATCH 04/10] switch to tokio::time::Duration --- examples/simple.rs | 2 +- examples/simple_closure.rs | 2 +- examples/simple_with_session.rs | 2 +- src/goose.rs | 98 ++++++++++++++++----------------- src/lib.rs | 8 +-- src/user.rs | 4 +- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 09f86581..50e3abdd 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -18,7 +18,7 @@ //! limitations under the License. use goose::prelude::*; -use std::time::Duration; +use tokio::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { diff --git a/examples/simple_closure.rs b/examples/simple_closure.rs index 3519ba5e..6f081fb2 100644 --- a/examples/simple_closure.rs +++ b/examples/simple_closure.rs @@ -19,7 +19,7 @@ use goose::prelude::*; use std::boxed::Box; use std::sync::Arc; -use std::time::Duration; +use tokio::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { diff --git a/examples/simple_with_session.rs b/examples/simple_with_session.rs index e077e42a..7208e730 100644 --- a/examples/simple_with_session.rs +++ b/examples/simple_with_session.rs @@ -19,7 +19,7 @@ use goose::prelude::*; use serde::Deserialize; -use std::time::Duration; +use tokio::time::Duration; struct Session { jwt_token: String, diff --git a/src/goose.rs b/src/goose.rs index 8269c37d..4bab1a37 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -63,7 +63,7 @@ //! //! ```rust //! use goose::prelude::*; -//! use std::time::Duration; +//! use tokio::time::Duration; //! //! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(3)).unwrap(); //! let mut bar_tasks = taskset!("BarTasks").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap(); @@ -473,10 +473,10 @@ pub struct GooseTaskSet { pub weight: usize, /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the /// minimum time a [`GooseUser`] will sleep after running a task. - pub min_wait: std::time::Duration, + pub min_wait: tokio::time::Duration, /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the /// maximum time a [`GooseUser`] will sleep after running a task. - pub max_wait: std::time::Duration, + pub max_wait: tokio::time::Duration, /// A vector containing one copy of each [`GooseTask`](./struct.GooseTask.html) that will /// run by users running this task set. pub tasks: Vec, @@ -512,8 +512,8 @@ impl GooseTaskSet { name: name.to_string(), task_sets_index: usize::max_value(), weight: 1, - min_wait: std::time::Duration::from_secs(0), - max_wait: std::time::Duration::from_secs(0), + min_wait: tokio::time::Duration::from_secs(0), + max_wait: tokio::time::Duration::from_secs(0), tasks: Vec::new(), weighted_tasks: Vec::new(), weighted_on_start_tasks: Vec::new(), @@ -602,7 +602,7 @@ impl GooseTaskSet { /// # Example /// ```rust /// use goose::prelude::*; - /// use std::time::Duration; + /// use tokio::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { @@ -613,8 +613,8 @@ impl GooseTaskSet { /// ``` pub fn set_wait_time( mut self, - min_wait: std::time::Duration, - max_wait: std::time::Duration, + min_wait: tokio::time::Duration, + max_wait: tokio::time::Duration, ) -> Result { trace!( "{} set_wait time: min: {:?} max: {:?}", @@ -747,9 +747,9 @@ pub struct GaggleUser { /// The base URL to prepend to all relative paths. pub base_url: Arc>, /// Minimum amount of time to sleep after running a task. - pub min_wait: std::time::Duration, + pub min_wait: tokio::time::Duration, /// Maximum amount of time to sleep after running a task. - pub max_wait: std::time::Duration, + pub max_wait: tokio::time::Duration, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// Load test hash. @@ -760,8 +760,8 @@ impl GaggleUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: std::time::Duration, - max_wait: std::time::Duration, + min_wait: tokio::time::Duration, + max_wait: tokio::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Self { @@ -856,9 +856,9 @@ pub struct GooseUser { /// The base URL to prepend to all relative paths. pub base_url: Url, /// Minimum amount of time to sleep after running a task. - pub min_wait: std::time::Duration, + pub min_wait: tokio::time::Duration, /// Maximum amount of time to sleep after running a task. - pub max_wait: std::time::Duration, + pub max_wait: tokio::time::Duration, /// A local copy of the global [`GooseConfiguration`](../struct.GooseConfiguration.html). pub config: GooseConfiguration, /// Channel to logger. @@ -892,8 +892,8 @@ impl GooseUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: std::time::Duration, - max_wait: std::time::Duration, + min_wait: tokio::time::Duration, + max_wait: tokio::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Result { @@ -932,8 +932,8 @@ impl GooseUser { let mut single_user = GooseUser::new( 0, base_url, - std::time::Duration::from_secs(0), - std::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), configuration, 0, )?; @@ -2273,7 +2273,7 @@ impl GooseUser { /// # Example /// ```rust /// use goose::prelude::*; - /// use std::time::Duration; + /// use tokio::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { @@ -2688,8 +2688,8 @@ mod tests { assert_eq!(task_set.name, "foo"); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); assert_eq!(task_set.tasks.len(), 0); assert_eq!(task_set.weighted_tasks.len(), 0); @@ -2702,8 +2702,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Different task can be registered. @@ -2712,8 +2712,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Same task can be registered again. @@ -2722,8 +2722,8 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Setting weight only affects weight field. @@ -2732,8 +2732,8 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); assert!(task_set.host.is_none()); // Weight can be changed. @@ -2747,8 +2747,8 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); // Host field can be changed. task_set = task_set.set_host("https://bar.example.com/"); @@ -2757,12 +2757,12 @@ mod tests { // Wait time only affects wait time fields. task_set = task_set .set_wait_time( - std::time::Duration::from_secs(1), - std::time::Duration::from_secs(10), + tokio::time::Duration::from_secs(1), + tokio::time::Duration::from_secs(10), ) .unwrap(); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(1)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(10)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(1)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(10)); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); assert_eq!(task_set.weight, 5); assert_eq!(task_set.tasks.len(), 3); @@ -2772,12 +2772,12 @@ mod tests { // Wait time can be changed. task_set = task_set .set_wait_time( - std::time::Duration::from_secs(3), - std::time::Duration::from_secs(9), + tokio::time::Duration::from_secs(3), + tokio::time::Duration::from_secs(9), ) .unwrap(); - assert_eq!(task_set.min_wait, std::time::Duration::from_secs(3)); - assert_eq!(task_set.max_wait, std::time::Duration::from_secs(9)); + assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(3)); + assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(9)); } #[test] @@ -2868,15 +2868,15 @@ mod tests { let user = GooseUser::new( 0, base_url, - std::time::Duration::from_secs(0), - std::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), &configuration, 0, ) .unwrap(); assert_eq!(user.task_sets_index, 0); - assert_eq!(user.min_wait, std::time::Duration::from_secs(0)); - assert_eq!(user.max_wait, std::time::Duration::from_secs(0)); + assert_eq!(user.min_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(user.max_wait, tokio::time::Duration::from_secs(0)); assert_eq!(user.weighted_users_index, usize::max_value()); // Confirm the URLs are correctly built using the default_host. @@ -2905,14 +2905,14 @@ mod tests { let user2 = GooseUser::new( 0, base_url, - std::time::Duration::from_secs(1), - std::time::Duration::from_secs(3), + tokio::time::Duration::from_secs(1), + tokio::time::Duration::from_secs(3), &configuration, 0, ) .unwrap(); - assert_eq!(user2.min_wait, std::time::Duration::from_secs(1)); - assert_eq!(user2.max_wait, std::time::Duration::from_secs(3)); + assert_eq!(user2.min_wait, tokio::time::Duration::from_secs(1)); + assert_eq!(user2.max_wait, tokio::time::Duration::from_secs(3)); // Confirm the URLs are correctly built using the task_set_host. let url = user2.build_url("/foo").unwrap(); @@ -2974,8 +2974,8 @@ mod tests { let user = GooseUser::new( 0, base_url, - std::time::Duration::from_secs(0), - std::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), + tokio::time::Duration::from_secs(0), &configuration, 0, ) diff --git a/src/lib.rs b/src/lib.rs index 8ae8f5a3..8cba4f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,13 +84,13 @@ //! helper, for example to set a timeout on this specific request: //! //! ```rust -//! use std::time; +//! use tokio::time::Duration; //! //! use goose::prelude::*; //! //! async fn loadtest_bar(user: &mut GooseUser) -> GooseTaskResult { //! let request_builder = user.goose_get("/path/to/bar")?; -//! let _goose = user.goose_send(request_builder.timeout(time::Duration::from_secs(3)), None).await?; +//! let _goose = user.goose_send(request_builder.timeout(Duration::from_secs(3)), None).await?; //! //! Ok(()) //! } @@ -550,9 +550,9 @@ pub enum GooseError { /// Invalid wait time specified. InvalidWaitTime { // The specified minimum wait time. - min_wait: std::time::Duration, + min_wait: tokio::time::Duration, // The specified maximum wait time. - max_wait: std::time::Duration, + max_wait: tokio::time::Duration, /// An optional explanation of the error. detail: String, }, diff --git a/src/user.rs b/src/user.rs index e4374c08..c1784966 100644 --- a/src/user.rs +++ b/src/user.rs @@ -75,7 +75,7 @@ pub(crate) async fn user_main( let wait_time = if thread_user.max_wait.as_millis() > 0 { rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) } else { - std::time::Duration::from_secs(0) + tokio::time::Duration::from_secs(0) }; // Counter to track how long we've slept, waking regularly to check for messages. @@ -102,7 +102,7 @@ pub(crate) async fn user_main( if thread_user.max_wait.as_millis() > 0 { let sleep_duration = if thread_user.max_wait.as_secs() >= 1 { slept += 1000; - std::time::Duration::from_secs(1) + tokio::time::Duration::from_secs(1) } else { slept += thread_user.max_wait.as_millis(); thread_user.max_wait From f83b0ca82308249a884aab36111913376bf5584a Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Mon, 11 Oct 2021 13:16:43 +0200 Subject: [PATCH 05/10] perform fewer duration conversions --- src/user.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/user.rs b/src/user.rs index c1784966..7bf13f02 100644 --- a/src/user.rs +++ b/src/user.rs @@ -55,6 +55,9 @@ pub(crate) async fn user_main( // Mitigation is enabled. thread_user.update_request_cadence(thread_number).await; + // When there is a delay between tasks, wake every second to check for messages. + let one_second = tokio::time::Duration::from_secs(1); + for (thread_task_index, thread_task_name) in &thread_task_set.weighted_tasks { // Determine which task we're going to run next. let function = &thread_task_set.tasks[*thread_task_index].function; @@ -77,6 +80,8 @@ pub(crate) async fn user_main( } else { tokio::time::Duration::from_secs(0) }; + // Also convert the duration to milliseconds. + let wait_time_millis = wait_time.as_millis(); // Counter to track how long we've slept, waking regularly to check for messages. let mut slept: u128 = 0; @@ -85,6 +90,7 @@ pub(crate) async fn user_main( let mut in_sleep_loop = true; // Track the time slept for Coordinated Omission Mitigation. let sleep_timer = time::Instant::now(); + while in_sleep_loop { let mut message = thread_receiver.try_recv(); while message.is_ok() { @@ -99,23 +105,28 @@ pub(crate) async fn user_main( } message = thread_receiver.try_recv(); } - if thread_user.max_wait.as_millis() > 0 { - let sleep_duration = if thread_user.max_wait.as_secs() >= 1 { + + if wait_time_millis > 0 { + let sleep_duration = if wait_time_millis - slept >= 1000 { slept += 1000; - tokio::time::Duration::from_secs(1) + if slept >= wait_time_millis { + // Break out of sleep loop after next sleep. + in_sleep_loop = false; + } + one_second } else { - slept += thread_user.max_wait.as_millis(); - thread_user.max_wait + slept += wait_time_millis; + // Break out of sleep loop after next sleep. + in_sleep_loop = false; + tokio::time::Duration::from_millis((wait_time_millis - slept) as u64) }; debug!( "user {} from {} sleeping {:?} ...", thread_number, thread_task_set.name, sleep_duration ); + tokio::time::sleep(sleep_duration).await; - if slept > wait_time.as_millis() { - in_sleep_loop = false; - } } else { in_sleep_loop = false; } From 027ed9e3d83b7b94927adc591293778cf4ed6d43 Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Tue, 12 Oct 2021 10:03:43 +0200 Subject: [PATCH 06/10] move calculations out of fast path --- src/user.rs | 61 +++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/user.rs b/src/user.rs index 7bf13f02..fe592a80 100644 --- a/src/user.rs +++ b/src/user.rs @@ -50,14 +50,14 @@ pub(crate) async fn user_main( // If normal tasks are defined, loop launching tasks until parent tells us to stop. if !thread_task_set.weighted_tasks.is_empty() { + // When there is a delay between tasks, wake every second to check for messages. + let one_second = tokio::time::Duration::from_secs(1); + 'launch_tasks: loop { // Tracks the time it takes to loop through all GooseTasks when Coordinated Omission // Mitigation is enabled. thread_user.update_request_cadence(thread_number).await; - // When there is a delay between tasks, wake every second to check for messages. - let one_second = tokio::time::Duration::from_secs(1); - for (thread_task_index, thread_task_name) in &thread_task_set.weighted_tasks { // Determine which task we're going to run next. let function = &thread_task_set.tasks[*thread_task_index].function; @@ -75,38 +75,37 @@ pub(crate) async fn user_main( .await; // Prepare to sleep for a random value from min_wait to max_wait. - let wait_time = if thread_user.max_wait.as_millis() > 0 { - rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) - } else { + let wait_time = if thread_user.max_wait.is_zero() { tokio::time::Duration::from_secs(0) + } else { + rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) }; // Also convert the duration to milliseconds. let wait_time_millis = wait_time.as_millis(); - // Counter to track how long we've slept, waking regularly to check for messages. - let mut slept: u128 = 0; - - // Wake every second to check if the parent thread has told us to exit. - let mut in_sleep_loop = true; - // Track the time slept for Coordinated Omission Mitigation. - let sleep_timer = time::Instant::now(); - - while in_sleep_loop { - let mut message = thread_receiver.try_recv(); - while message.is_ok() { - match message.unwrap() { - // Time to exit, break out of launch_tasks loop. - GooseUserCommand::Exit => { - break 'launch_tasks; - } - command => { - debug!("ignoring unexpected GooseUserCommand: {:?}", command); + if wait_time_millis > 0 { + // Counter to track how long we've slept, waking regularly to check for messages. + let mut slept: u128 = 0; + // Wake every second to check if the parent thread has told us to exit. + let mut in_sleep_loop = true; + // Track the time slept for Coordinated Omission Mitigation. + let sleep_timer = time::Instant::now(); + + while in_sleep_loop { + let mut message = thread_receiver.try_recv(); + while message.is_ok() { + match message.unwrap() { + // Time to exit, break out of launch_tasks loop. + GooseUserCommand::Exit => { + break 'launch_tasks; + } + command => { + debug!("ignoring unexpected GooseUserCommand: {:?}", command); + } } + message = thread_receiver.try_recv(); } - message = thread_receiver.try_recv(); - } - if wait_time_millis > 0 { let sleep_duration = if wait_time_millis - slept >= 1000 { slept += 1000; if slept >= wait_time_millis { @@ -127,13 +126,11 @@ pub(crate) async fn user_main( ); tokio::time::sleep(sleep_duration).await; - } else { - in_sleep_loop = false; } + // Track how much time the GooseUser sleeps during this loop through all GooseTasks, + // used by Coordinated Omission Mitigation. + thread_user.slept += (time::Instant::now() - sleep_timer).as_millis() as u64; } - // Track how much time the GooseUser sleeps during this loop through all GooseTasks, - // used by Coordinated Omission Mitigation. - thread_user.slept += (time::Instant::now() - sleep_timer).as_millis() as u64; } } } From 6be51e9c53f99a692cf05e344d586149a19048ab Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Tue, 12 Oct 2021 10:27:54 +0200 Subject: [PATCH 07/10] make it possible to break out of loop --- src/user.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/user.rs b/src/user.rs index fe592a80..bc0d13ed 100644 --- a/src/user.rs +++ b/src/user.rs @@ -74,6 +74,20 @@ pub(crate) async fn user_main( ) .await; + let mut message = thread_receiver.try_recv(); + while message.is_ok() { + match message.unwrap() { + // Time to exit, break out of launch_tasks loop. + GooseUserCommand::Exit => { + break 'launch_tasks; + } + command => { + info!("ignoring unexpected GooseUserCommand: {:?}", command); + } + } + message = thread_receiver.try_recv(); + } + // Prepare to sleep for a random value from min_wait to max_wait. let wait_time = if thread_user.max_wait.is_zero() { tokio::time::Duration::from_secs(0) @@ -92,7 +106,7 @@ pub(crate) async fn user_main( let sleep_timer = time::Instant::now(); while in_sleep_loop { - let mut message = thread_receiver.try_recv(); + message = thread_receiver.try_recv(); while message.is_ok() { match message.unwrap() { // Time to exit, break out of launch_tasks loop. From b12b14bcaee7bae16d0b03cac641e8f694d7f179 Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Tue, 12 Oct 2021 12:19:59 +0200 Subject: [PATCH 08/10] optimize fast path --- CHANGELOG.md | 1 + src/goose.rs | 107 ++++++++++++------------------------------------- src/lib.rs | 4 -- src/manager.rs | 6 --- src/user.rs | 71 +++++++++++++++----------------- src/worker.rs | 2 - 6 files changed, 58 insertions(+), 133 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39bb688e..5b4fc912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.14.1-dev - [#364](https://github.com/tag1consulting/goose/pull/364) add link from the [Developer Documentation](https://docs.rs/goose) to [The Git Book](https://book.goose.rs) + - [#368](https://github.com/tag1consulting/goose/pull/368) optimize fastpath if no delay between tasks ## 0.14.0 September 15, 2021 - [#361](https://github.com/tag1consulting/goose/pull/361) convert `README.md` (and enhance) into [`The Goose Book`](https://book.goose.rs/) diff --git a/src/goose.rs b/src/goose.rs index 4bab1a37..e11297df 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -296,6 +296,7 @@ use std::sync::Arc; use std::{fmt, str}; use std::{future::Future, pin::Pin, time::Instant}; use tokio::sync::RwLock; +use tokio::time::Duration; use url::Url; use crate::logger::GooseLog; @@ -471,12 +472,9 @@ pub struct GooseTaskSet { pub task_sets_index: usize, /// An integer value that controls the frequency that this task set will be assigned to a user. pub weight: usize, - /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the - /// minimum time a [`GooseUser`] will sleep after running a task. - pub min_wait: tokio::time::Duration, - /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) indicating the - /// maximum time a [`GooseUser`] will sleep after running a task. - pub max_wait: tokio::time::Duration, + /// A [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) range defining the + /// minimum and maximum time a [`GooseUser`] should sleep after running a task. + pub task_wait: Option<(Duration, Duration)>, /// A vector containing one copy of each [`GooseTask`](./struct.GooseTask.html) that will /// run by users running this task set. pub tasks: Vec, @@ -512,8 +510,7 @@ impl GooseTaskSet { name: name.to_string(), task_sets_index: usize::max_value(), weight: 1, - min_wait: tokio::time::Duration::from_secs(0), - max_wait: tokio::time::Duration::from_secs(0), + task_wait: None, tasks: Vec::new(), weighted_tasks: Vec::new(), weighted_on_start_tasks: Vec::new(), @@ -622,7 +619,7 @@ impl GooseTaskSet { min_wait, max_wait ); - if min_wait > max_wait { + if min_wait.as_millis() > max_wait.as_millis() { return Err(GooseError::InvalidWaitTime { min_wait, max_wait, @@ -631,8 +628,7 @@ impl GooseTaskSet { .to_string(), }); } - self.min_wait = min_wait; - self.max_wait = max_wait; + self.task_wait = Some((min_wait, max_wait)); Ok(self) } @@ -746,10 +742,6 @@ pub struct GaggleUser { pub task_sets_index: usize, /// The base URL to prepend to all relative paths. pub base_url: Arc>, - /// Minimum amount of time to sleep after running a task. - pub min_wait: tokio::time::Duration, - /// Maximum amount of time to sleep after running a task. - pub max_wait: tokio::time::Duration, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// Load test hash. @@ -760,8 +752,6 @@ impl GaggleUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: tokio::time::Duration, - max_wait: tokio::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Self { @@ -769,8 +759,6 @@ impl GaggleUser { GaggleUser { task_sets_index, base_url: Arc::new(RwLock::new(base_url)), - min_wait, - max_wait, config: configuration.clone(), load_test_hash, } @@ -855,10 +843,6 @@ pub struct GooseUser { pub client: Client, /// The base URL to prepend to all relative paths. pub base_url: Url, - /// Minimum amount of time to sleep after running a task. - pub min_wait: tokio::time::Duration, - /// Maximum amount of time to sleep after running a task. - pub max_wait: tokio::time::Duration, /// A local copy of the global [`GooseConfiguration`](../struct.GooseConfiguration.html). pub config: GooseConfiguration, /// Channel to logger. @@ -892,8 +876,6 @@ impl GooseUser { pub fn new( task_sets_index: usize, base_url: Url, - min_wait: tokio::time::Duration, - max_wait: tokio::time::Duration, configuration: &GooseConfiguration, load_test_hash: u64, ) -> Result { @@ -910,8 +892,6 @@ impl GooseUser { task_sets_index, client, base_url, - min_wait, - max_wait, config: configuration.clone(), logger: None, throttle: None, @@ -929,14 +909,7 @@ impl GooseUser { /// Create a new single-use user. pub fn single(base_url: Url, configuration: &GooseConfiguration) -> Result { - let mut single_user = GooseUser::new( - 0, - base_url, - tokio::time::Duration::from_secs(0), - tokio::time::Duration::from_secs(0), - configuration, - 0, - )?; + let mut single_user = GooseUser::new(0, base_url, configuration, 0)?; // Only one user, so index is 0. single_user.weighted_users_index = 0; // Do not throttle [`test_start`](../struct.GooseAttack.html#method.test_start) (setup) and @@ -2688,8 +2661,7 @@ mod tests { assert_eq!(task_set.name, "foo"); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); assert!(task_set.host.is_none()); assert_eq!(task_set.tasks.len(), 0); assert_eq!(task_set.weighted_tasks.len(), 0); @@ -2702,8 +2674,7 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); assert!(task_set.host.is_none()); // Different task can be registered. @@ -2712,8 +2683,7 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); assert!(task_set.host.is_none()); // Same task can be registered again. @@ -2722,8 +2692,7 @@ mod tests { assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); assert_eq!(task_set.weight, 1); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); assert!(task_set.host.is_none()); // Setting weight only affects weight field. @@ -2732,8 +2701,7 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); assert!(task_set.host.is_none()); // Weight can be changed. @@ -2747,8 +2715,7 @@ mod tests { assert_eq!(task_set.tasks.len(), 3); assert_eq!(task_set.weighted_tasks.len(), 0); assert_eq!(task_set.task_sets_index, usize::max_value()); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(0)); + assert_eq!(task_set.task_wait, None); // Host field can be changed. task_set = task_set.set_host("https://bar.example.com/"); @@ -2761,8 +2728,10 @@ mod tests { tokio::time::Duration::from_secs(10), ) .unwrap(); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(1)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(10)); + assert_eq!( + task_set.task_wait, + Some((Duration::from_secs(1), Duration::from_secs(10))) + ); assert_eq!(task_set.host, Some("https://bar.example.com/".to_string())); assert_eq!(task_set.weight, 5); assert_eq!(task_set.tasks.len(), 3); @@ -2776,8 +2745,10 @@ mod tests { tokio::time::Duration::from_secs(9), ) .unwrap(); - assert_eq!(task_set.min_wait, tokio::time::Duration::from_secs(3)); - assert_eq!(task_set.max_wait, tokio::time::Duration::from_secs(9)); + assert_eq!( + task_set.task_wait, + Some((Duration::from_secs(3), Duration::from_secs(9))) + ); } #[test] @@ -2865,18 +2836,8 @@ mod tests { const HOST: &str = "http://example.com/"; let configuration = GooseConfiguration::parse_args_default(&EMPTY_ARGS).unwrap(); let base_url = get_base_url(Some(HOST.to_string()), None, None).unwrap(); - let user = GooseUser::new( - 0, - base_url, - tokio::time::Duration::from_secs(0), - tokio::time::Duration::from_secs(0), - &configuration, - 0, - ) - .unwrap(); + let user = GooseUser::new(0, base_url, &configuration, 0).unwrap(); assert_eq!(user.task_sets_index, 0); - assert_eq!(user.min_wait, tokio::time::Duration::from_secs(0)); - assert_eq!(user.max_wait, tokio::time::Duration::from_secs(0)); assert_eq!(user.weighted_users_index, usize::max_value()); // Confirm the URLs are correctly built using the default_host. @@ -2902,17 +2863,7 @@ mod tests { Some("http://www.example.com/".to_string()), ) .unwrap(); - let user2 = GooseUser::new( - 0, - base_url, - tokio::time::Duration::from_secs(1), - tokio::time::Duration::from_secs(3), - &configuration, - 0, - ) - .unwrap(); - assert_eq!(user2.min_wait, tokio::time::Duration::from_secs(1)); - assert_eq!(user2.max_wait, tokio::time::Duration::from_secs(3)); + let user2 = GooseUser::new(0, base_url, &configuration, 0).unwrap(); // Confirm the URLs are correctly built using the task_set_host. let url = user2.build_url("/foo").unwrap(); @@ -2971,15 +2922,7 @@ mod tests { // Confirm Goose can build a base_url that includes a path. const HOST_WITH_PATH: &str = "http://example.com/with/path/"; let base_url = get_base_url(Some(HOST_WITH_PATH.to_string()), None, None).unwrap(); - let user = GooseUser::new( - 0, - base_url, - tokio::time::Duration::from_secs(0), - tokio::time::Duration::from_secs(0), - &configuration, - 0, - ) - .unwrap(); + let user = GooseUser::new(0, base_url, &configuration, 0).unwrap(); // Confirm the URLs are correctly built using the default_host that includes a path. let url = user.build_url("foo").unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 8cba4f7c..f13fc9d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1137,8 +1137,6 @@ impl GooseAttack { weighted_users.push(GooseUser::new( self.task_sets[*task_sets_index].task_sets_index, base_url, - self.task_sets[*task_sets_index].min_wait, - self.task_sets[*task_sets_index].max_wait, &self.configuration, self.metrics.hash, )?); @@ -1172,8 +1170,6 @@ impl GooseAttack { weighted_users.push(GaggleUser::new( self.task_sets[*task_sets_index].task_sets_index, base_url, - self.task_sets[*task_sets_index].min_wait, - self.task_sets[*task_sets_index].max_wait, &self.configuration, self.metrics.hash, )); diff --git a/src/manager.rs b/src/manager.rs index 775a1bbd..a0af5836 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -25,10 +25,6 @@ pub struct GooseUserInitializer { pub task_sets_index: usize, /// The base_url for this user thread. pub base_url: String, - /// Minimum amount of time to sleep after running a task. - pub min_wait: std::time::Duration, - /// Maximum amount of time to sleep after running a task. - pub max_wait: std::time::Duration, /// A local copy of the global GooseConfiguration. pub config: GooseConfiguration, /// How long the load test should run, in seconds. @@ -474,8 +470,6 @@ pub(crate) async fn manager_main(mut goose_attack: GooseAttack) -> GooseAttack { users.push(GooseUserInitializer { task_sets_index: user.task_sets_index, base_url: user.base_url.read().await.to_string(), - min_wait: user.min_wait, - max_wait: user.max_wait, config: user.config.clone(), run_time: goose_attack.run_time, worker_id: workers.len(), diff --git a/src/user.rs b/src/user.rs index bc0d13ed..ec335082 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,5 +1,6 @@ use rand::Rng; use std::time; +use tokio::time::Duration; use crate::get_worker_id; use crate::goose::{GooseTaskFunction, GooseTaskSet, GooseUser, GooseUserCommand}; @@ -51,7 +52,7 @@ pub(crate) async fn user_main( // If normal tasks are defined, loop launching tasks until parent tells us to stop. if !thread_task_set.weighted_tasks.is_empty() { // When there is a delay between tasks, wake every second to check for messages. - let one_second = tokio::time::Duration::from_secs(1); + let one_second = Duration::from_secs(1); 'launch_tasks: loop { // Tracks the time it takes to loop through all GooseTasks when Coordinated Omission @@ -74,30 +75,13 @@ pub(crate) async fn user_main( ) .await; - let mut message = thread_receiver.try_recv(); - while message.is_ok() { - match message.unwrap() { - // Time to exit, break out of launch_tasks loop. - GooseUserCommand::Exit => { - break 'launch_tasks; - } - command => { - info!("ignoring unexpected GooseUserCommand: {:?}", command); - } - } - message = thread_receiver.try_recv(); + if received_exit(&thread_receiver) { + break 'launch_tasks; } - // Prepare to sleep for a random value from min_wait to max_wait. - let wait_time = if thread_user.max_wait.is_zero() { - tokio::time::Duration::from_secs(0) - } else { - rand::thread_rng().gen_range(thread_user.min_wait..thread_user.max_wait) - }; - // Also convert the duration to milliseconds. - let wait_time_millis = wait_time.as_millis(); - - if wait_time_millis > 0 { + // If the task_wait is defined, wait for a random time between tasks. + if let Some((min, max)) = thread_task_set.task_wait { + let wait_time = rand::thread_rng().gen_range(min..max).as_millis(); // Counter to track how long we've slept, waking regularly to check for messages. let mut slept: u128 = 0; // Wake every second to check if the parent thread has told us to exit. @@ -106,32 +90,22 @@ pub(crate) async fn user_main( let sleep_timer = time::Instant::now(); while in_sleep_loop { - message = thread_receiver.try_recv(); - while message.is_ok() { - match message.unwrap() { - // Time to exit, break out of launch_tasks loop. - GooseUserCommand::Exit => { - break 'launch_tasks; - } - command => { - debug!("ignoring unexpected GooseUserCommand: {:?}", command); - } - } - message = thread_receiver.try_recv(); + if received_exit(&thread_receiver) { + break 'launch_tasks; } - let sleep_duration = if wait_time_millis - slept >= 1000 { + let sleep_duration = if wait_time - slept >= 1000 { slept += 1000; - if slept >= wait_time_millis { + if slept >= wait_time { // Break out of sleep loop after next sleep. in_sleep_loop = false; } one_second } else { - slept += wait_time_millis; + slept += wait_time; // Break out of sleep loop after next sleep. in_sleep_loop = false; - tokio::time::Duration::from_millis((wait_time_millis - slept) as u64) + tokio::time::Duration::from_millis((wait_time - slept) as u64) }; debug!( @@ -186,6 +160,25 @@ pub(crate) async fn user_main( } } +// Determine if the parent has sent a GooseUserCommand::Exit message. +fn received_exit(thread_receiver: &flume::Receiver) -> bool { + let mut message = thread_receiver.try_recv(); + while message.is_ok() { + match message.unwrap() { + // GooseUserCommand::Exit received. + GooseUserCommand::Exit => { + return true; + } + command => { + debug!("ignoring unexpected GooseUserCommand: {:?}", command); + } + } + message = thread_receiver.try_recv(); + } + // GooseUserCommand::Exit not received. + false +} + // Invoke the task function, collecting task metrics. async fn invoke_task_function( function: &GooseTaskFunction, diff --git a/src/worker.rs b/src/worker.rs index db30449b..ded9060d 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -139,8 +139,6 @@ pub(crate) async fn worker_main(goose_attack: GooseAttack) -> GooseAttack { let user = GooseUser::new( initializer.task_sets_index, Url::parse(&initializer.base_url).unwrap(), - initializer.min_wait, - initializer.max_wait, &initializer.config, goose_attack.metrics.hash, ) From 81d4668c726f7216744defb9b86571159cda9886 Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Wed, 13 Oct 2021 09:01:25 +0200 Subject: [PATCH 09/10] consistently use std::time::Duration --- examples/simple.rs | 2 +- examples/simple_closure.rs | 2 +- examples/simple_with_session.rs | 2 +- src/goose.rs | 22 ++++++++-------------- src/lib.rs | 14 +++++++------- src/user.rs | 4 ++-- src/util.rs | 6 +++--- 7 files changed, 23 insertions(+), 29 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 50e3abdd..09f86581 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -18,7 +18,7 @@ //! limitations under the License. use goose::prelude::*; -use tokio::time::Duration; +use std::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { diff --git a/examples/simple_closure.rs b/examples/simple_closure.rs index 6f081fb2..3519ba5e 100644 --- a/examples/simple_closure.rs +++ b/examples/simple_closure.rs @@ -19,7 +19,7 @@ use goose::prelude::*; use std::boxed::Box; use std::sync::Arc; -use tokio::time::Duration; +use std::time::Duration; #[tokio::main] async fn main() -> Result<(), GooseError> { diff --git a/examples/simple_with_session.rs b/examples/simple_with_session.rs index 7208e730..e077e42a 100644 --- a/examples/simple_with_session.rs +++ b/examples/simple_with_session.rs @@ -19,7 +19,7 @@ use goose::prelude::*; use serde::Deserialize; -use tokio::time::Duration; +use std::time::Duration; struct Session { jwt_token: String, diff --git a/src/goose.rs b/src/goose.rs index e11297df..833ed171 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -63,7 +63,7 @@ //! //! ```rust //! use goose::prelude::*; -//! use tokio::time::Duration; +//! use std::time::Duration; //! //! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(3)).unwrap(); //! let mut bar_tasks = taskset!("BarTasks").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap(); @@ -293,10 +293,10 @@ use reqwest::{header, Client, ClientBuilder, RequestBuilder, Response}; use serde::{Deserialize, Serialize}; use std::hash::{Hash, Hasher}; use std::sync::Arc; +use std::time::Duration; use std::{fmt, str}; use std::{future::Future, pin::Pin, time::Instant}; use tokio::sync::RwLock; -use tokio::time::Duration; use url::Url; use crate::logger::GooseLog; @@ -599,7 +599,7 @@ impl GooseTaskSet { /// # Example /// ```rust /// use goose::prelude::*; - /// use tokio::time::Duration; + /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { @@ -610,8 +610,8 @@ impl GooseTaskSet { /// ``` pub fn set_wait_time( mut self, - min_wait: tokio::time::Duration, - max_wait: tokio::time::Duration, + min_wait: std::time::Duration, + max_wait: std::time::Duration, ) -> Result { trace!( "{} set_wait time: min: {:?} max: {:?}", @@ -2246,7 +2246,7 @@ impl GooseUser { /// # Example /// ```rust /// use goose::prelude::*; - /// use tokio::time::Duration; + /// use std::time::Duration; /// /// #[tokio::main] /// async fn main() -> Result<(), GooseError> { @@ -2723,10 +2723,7 @@ mod tests { // Wait time only affects wait time fields. task_set = task_set - .set_wait_time( - tokio::time::Duration::from_secs(1), - tokio::time::Duration::from_secs(10), - ) + .set_wait_time(Duration::from_secs(1), Duration::from_secs(10)) .unwrap(); assert_eq!( task_set.task_wait, @@ -2740,10 +2737,7 @@ mod tests { // Wait time can be changed. task_set = task_set - .set_wait_time( - tokio::time::Duration::from_secs(3), - tokio::time::Duration::from_secs(9), - ) + .set_wait_time(Duration::from_secs(3), Duration::from_secs(9)) .unwrap(); assert_eq!( task_set.task_wait, diff --git a/src/lib.rs b/src/lib.rs index f13fc9d7..8061c4c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,7 @@ //! helper, for example to set a timeout on this specific request: //! //! ```rust -//! use tokio::time::Duration; +//! use std::time::Duration; //! //! use goose::prelude::*; //! @@ -550,9 +550,9 @@ pub enum GooseError { /// Invalid wait time specified. InvalidWaitTime { // The specified minimum wait time. - min_wait: tokio::time::Duration, + min_wait: std::time::Duration, // The specified maximum wait time. - max_wait: tokio::time::Duration, + max_wait: std::time::Duration, /// An optional explanation of the error. detail: String, }, @@ -1804,9 +1804,9 @@ impl GooseAttack { { let sleep_delay = self.configuration.running_metrics.unwrap() * 1_000; goose_attack_run_state.spawn_user_in_ms -= sleep_delay; - tokio::time::Duration::from_millis(sleep_delay as u64) + std::time::Duration::from_millis(sleep_delay as u64) } else { - tokio::time::Duration::from_millis(goose_attack_run_state.spawn_user_in_ms as u64) + std::time::Duration::from_millis(goose_attack_run_state.spawn_user_in_ms as u64) }; debug!("sleeping {:?}...", sleep_duration); goose_attack_run_state.drift_timer = @@ -1816,7 +1816,7 @@ impl GooseAttack { // If enough users have been spawned, move onto the next attack phase. if self.weighted_users.is_empty() { // Pause a tenth of a second waiting for the final user to fully start up. - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + tokio::time::sleep(std::time::Duration::from_millis(100)).await; if self.attack_mode == AttackMode::Worker { info!( @@ -2049,7 +2049,7 @@ impl GooseAttack { if self.configuration.no_autostart { // Sleep then check for further instructions. if goose_attack_run_state.idle_status_displayed { - let sleep_duration = tokio::time::Duration::from_millis(250); + let sleep_duration = std::time::Duration::from_millis(250); debug!("sleeping {:?}...", sleep_duration); goose_attack_run_state.drift_timer = util::sleep_minus_drift( sleep_duration, diff --git a/src/user.rs b/src/user.rs index ec335082..e1e9b6f9 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,6 +1,6 @@ use rand::Rng; use std::time; -use tokio::time::Duration; +use std::time::Duration; use crate::get_worker_id; use crate::goose::{GooseTaskFunction, GooseTaskSet, GooseUser, GooseUserCommand}; @@ -105,7 +105,7 @@ pub(crate) async fn user_main( slept += wait_time; // Break out of sleep loop after next sleep. in_sleep_loop = false; - tokio::time::Duration::from_millis((wait_time - slept) as u64) + Duration::from_millis((wait_time - slept) as u64) }; debug!( diff --git a/src/util.rs b/src/util.rs index dbd04a3b..28400680 100644 --- a/src/util.rs +++ b/src/util.rs @@ -82,11 +82,11 @@ pub fn parse_timespan(time_str: &str) -> usize { /// /// // Do other stuff, in this case sleep 250 milliseconds. This is /// // the "drift" that will be subtracted from the sleep time later. -/// tokio::time::sleep(tokio::time::Duration::from_millis(250)); +/// tokio::time::sleep(std::time::Duration::from_millis(250)); /// /// // Sleep for 1 second minus the time spent doing other stuff. /// drift_timer = util::sleep_minus_drift( -/// tokio::time::Duration::from_secs(1), +/// std::time::Duration::from_secs(1), /// drift_timer, /// ).await; /// @@ -98,7 +98,7 @@ pub fn parse_timespan(time_str: &str) -> usize { /// } /// ``` pub async fn sleep_minus_drift( - duration: tokio::time::Duration, + duration: std::time::Duration, drift: tokio::time::Instant, ) -> tokio::time::Instant { match duration.checked_sub(drift.elapsed()) { From a4ba01383169791a7a7c7aeb967577b99d405e1c Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Wed, 13 Oct 2021 10:45:50 +0200 Subject: [PATCH 10/10] consistency and documentation cleanups --- src/goose.rs | 15 +++++++-------- src/lib.rs | 15 ++++++++------- src/user.rs | 3 +-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/goose.rs b/src/goose.rs index 833ed171..65c4a509 100644 --- a/src/goose.rs +++ b/src/goose.rs @@ -55,17 +55,17 @@ //! //! ### Task Set Wait Time //! -//! Wait time is specified as a low-high integer range. Each time a task completes in -//! the task set, the user will pause for a random number of seconds inclusively between +//! Wait time is specified as a low-high Duration range. Each time a task completes in the +//! task set, the user will pause for a random number of milliseconds inclusively between //! the low and high wait times. In the following example, users loading `foo` tasks will -//! sleep 0 to 3 seconds after each task completes, and users loading `bar` tasks will +//! sleep 0 to 2.5 seconds after each task completes, and users loading `bar` tasks will //! sleep 5 to 10 seconds after each task completes. //! //! ```rust //! use goose::prelude::*; //! use std::time::Duration; //! -//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_secs(3)).unwrap(); +//! let mut foo_tasks = taskset!("FooTasks").set_wait_time(Duration::from_secs(0), Duration::from_millis(2500)).unwrap(); //! let mut bar_tasks = taskset!("BarTasks").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap(); //! ``` //! ## Creating Tasks @@ -593,8 +593,7 @@ impl GooseTaskSet { } /// Configure a task_set to to pause after running each task. The length of the pause will be randomly - /// selected from `min_weight` to `max_wait` inclusively. For example, if `min_wait` is `0` and - /// `max_weight` is `2`, the user will randomly sleep for 0, 1 or 2 seconds after each task completes. + /// selected from `min_wait` to `max_wait` inclusively. /// /// # Example /// ```rust @@ -610,8 +609,8 @@ impl GooseTaskSet { /// ``` pub fn set_wait_time( mut self, - min_wait: std::time::Duration, - max_wait: std::time::Duration, + min_wait: Duration, + max_wait: Duration, ) -> Result { trace!( "{} set_wait time: min: {:?} max: {:?}", diff --git a/src/lib.rs b/src/lib.rs index 8061c4c3..276a7c78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -468,7 +468,8 @@ use std::sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }; -use std::{fmt, io, time}; +use std::time::{self, Duration}; +use std::{fmt, io}; use tokio::fs::File; use crate::config::{GooseConfiguration, GooseDefaults}; @@ -550,9 +551,9 @@ pub enum GooseError { /// Invalid wait time specified. InvalidWaitTime { // The specified minimum wait time. - min_wait: std::time::Duration, + min_wait: Duration, // The specified maximum wait time. - max_wait: std::time::Duration, + max_wait: Duration, /// An optional explanation of the error. detail: String, }, @@ -1804,9 +1805,9 @@ impl GooseAttack { { let sleep_delay = self.configuration.running_metrics.unwrap() * 1_000; goose_attack_run_state.spawn_user_in_ms -= sleep_delay; - std::time::Duration::from_millis(sleep_delay as u64) + Duration::from_millis(sleep_delay as u64) } else { - std::time::Duration::from_millis(goose_attack_run_state.spawn_user_in_ms as u64) + Duration::from_millis(goose_attack_run_state.spawn_user_in_ms as u64) }; debug!("sleeping {:?}...", sleep_duration); goose_attack_run_state.drift_timer = @@ -1816,7 +1817,7 @@ impl GooseAttack { // If enough users have been spawned, move onto the next attack phase. if self.weighted_users.is_empty() { // Pause a tenth of a second waiting for the final user to fully start up. - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + tokio::time::sleep(Duration::from_millis(100)).await; if self.attack_mode == AttackMode::Worker { info!( @@ -2049,7 +2050,7 @@ impl GooseAttack { if self.configuration.no_autostart { // Sleep then check for further instructions. if goose_attack_run_state.idle_status_displayed { - let sleep_duration = std::time::Duration::from_millis(250); + let sleep_duration = Duration::from_millis(250); debug!("sleeping {:?}...", sleep_duration); goose_attack_run_state.drift_timer = util::sleep_minus_drift( sleep_duration, diff --git a/src/user.rs b/src/user.rs index e1e9b6f9..98e22925 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,6 +1,5 @@ use rand::Rng; -use std::time; -use std::time::Duration; +use std::time::{self, Duration}; use crate::get_worker_id; use crate::goose::{GooseTaskFunction, GooseTaskSet, GooseUser, GooseUserCommand};