Expand description
Helpers and objects for building Goose load tests.
Goose manages load tests with a series of objects:
Scenario
each user is assigned a scenario, which is a collection of transactions.Transaction
transactions define one or more web requests and are assigned to scenarios.GooseUser
a user state responsible for repeatedly running all transactions in the assigned scenario.GooseRequestMetric
optional metrics collected for each URL/method pair.
Creating Scenarios
A Scenario
is created by passing in a &str
name to the new
function, for example:
use goose::prelude::*;
let mut loadtest_transactions = scenario!("LoadtestTransactions");
Scenario Weight
A weight can be applied to a scenario, controlling how often it is assigned to
GooseUser
threads. The larger the integer value
of weight, the more the scenario will be assigned to user threads. In the following
example, FooTransactions
will be assigned to users twice as often as Bar
Transactions. We could have just added a weight of 2
to FooTransactions
and left
the default weight of 1
assigned to BarTransactions
for the same weighting:
use goose::prelude::*;
#[tokio::main]
async fn main() -> Result<(), GooseError> {
let mut foo_transactions = scenario!("FooTransactions").set_weight(10)?;
let mut bar_transactions = scenario!("BarTransactions").set_weight(5)?;
Ok(())
}
Scenario Host
A default host can be assigned to a scenario, which will be used only if the --host
CLI option is not set at run-time. For example, this can configure your load test to
run against your local development environment by default, allowing the --host
option
to override host when you want to load test production. You can also assign different
hosts to different scenario if this is desirable:
use goose::prelude::*;
let mut foo_transactions = scenario!("FooTransactions").set_host("http://www.local");
let mut bar_transactions = scenario!("BarTransactions").set_host("http://www2.local");
Scenario Wait Time
Wait time is specified as a low-high Duration range. Each time a transaction completes in the
scenario, 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
transactions will
sleep 0 to 2.5 seconds after each transaction completes, and users loading bar
transactions will
sleep 5 to 10 seconds after each transaction completes.
use goose::prelude::*;
use std::time::Duration;
let mut foo_transactions = scenario!("FooTransactions").set_wait_time(Duration::from_secs(0), Duration::from_millis(2500)).unwrap();
let mut bar_transactions = scenario!("BarTransactions").set_wait_time(Duration::from_secs(5), Duration::from_secs(10)).unwrap();
Creating Transactions
A Transaction
must include a pointer to a function which
will be executed each time the transaction is run.
use goose::prelude::*;
let mut a_transaction = transaction!(transaction_function);
/// A very simple transaction that loads the front page.
async fn transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("").await?;
Ok(())
}
Transaction Name
A name can be assigned to a transaction, and will be displayed in metrics about all requests made by the transaction.
use goose::prelude::*;
let mut a_transaction = transaction!(transaction_function).set_name("a");
/// A very simple transaction that loads the front page.
async fn transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("").await?;
Ok(())
}
Transaction Weight
Individual transactions can be assigned a weight, controlling how often the transaction runs. The
larger the value of weight, the more it will run. In the following example, a_transaction
runs 3 times as often as b_transaction
:
use goose::prelude::*;
#[tokio::main]
async fn main() -> Result<(), GooseError> {
let mut a_transaction = transaction!(a_transaction_function).set_weight(9)?;
let mut b_transaction = transaction!(b_transaction_function).set_weight(3)?;
Ok(())
}
/// A very simple transaction that loads the "a" page.
async fn a_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("a/").await?;
Ok(())
}
/// Another very simple transaction that loads the "b" page.
async fn b_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("b/").await?;
Ok(())
}
Transaction Sequence
Transactions can also be configured to run in a sequence. For example, a transaction with a
sequence value of 1
will always run before a transaction with a sequence value of 2
. Weight can
be applied to sequenced transactions, so for example a transaction with a weight of 2
and a sequence
of 1
will run two times before a transaction with a sequence of 2
. Scenarios can contain
transactions with sequence values and without sequence values, and in this case all transactions with
a sequence value will run before transactions without a sequence value. In the following example,
a_transaction
runs before b_transaction
, which runs before c_transaction
:
use goose::prelude::*;
let mut a_transaction = transaction!(a_transaction_function).set_sequence(1);
let mut b_transaction = transaction!(b_transaction_function).set_sequence(2);
let mut c_transaction = transaction!(c_transaction_function);
/// A very simple transaction that loads the "a" page.
async fn a_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("a/").await?;
Ok(())
}
/// Another very simple transaction that loads the "b" page.
async fn b_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("b/").await?;
Ok(())
}
/// Another very simple transaction that loads the "c" page.
async fn c_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("c/").await?;
Ok(())
}
Transaction On Start
Transactions can be flagged to only run when a user first starts. This can be useful if you’d
like your load test to use a logged-in user. It is possible to assign sequences and weights
to on_start
functions if you want to have
multiple transactions run in a specific order at start time, and/or the transactions to run multiple times.
A transaction can be flagged to run both on start and on stop.
use goose::prelude::*;
let mut a_transaction = transaction!(a_transaction_function).set_sequence(1).set_on_start();
/// A very simple transaction that loads the "a" page.
async fn a_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("a/").await?;
Ok(())
}
Transaction On Stop
Transactions can be flagged to only run when a user stops. This can be useful if you’d like your
load test to simulate a user logging out when it finishes. It is possible to assign sequences
and weights to on_stop
functions if you want to
have multiple transactions run in a specific order at stop time, and/or the transactions to run multiple
times. A transaction can be flagged to run both on start and on stop.
use goose::prelude::*;
let mut b_transaction = transaction!(b_transaction_function).set_sequence(2).set_on_stop();
/// Another very simple transaction that loads the "b" page.
async fn b_transaction_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("b/").await?;
Ok(())
}
Controlling User
When Goose starts, it creates one or more GooseUser
s,
assigning a single Scenario
to each. This user is
then used to generate load. Behind the scenes, Goose is leveraging the
reqwest::client
to load web pages, and Goose can therefor do anything reqwest
can do.
The most common request types are GET
and
POST
, but HEAD
,
PUT, PATCH and DELETE
are also supported.
GET
A helper to make a GET
request of a path and collect relevant metrics.
Automatically prepends the correct host.
use goose::prelude::*;
let mut transaction = transaction!(get_function);
/// A very simple transaction that makes a GET request.
async fn get_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("path/to/foo/").await?;
Ok(())
}
The returned response is a reqwest::Response
struct. You can use it as you would any Reqwest Response.
POST
A helper to make a POST
request of a string value to the path and collect relevant
metrics. Automatically prepends the correct host. The returned response is a
reqwest::Response
use goose::prelude::*;
let mut transaction = transaction!(post_function);
/// A very simple transaction that makes a POST request.
async fn post_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.post("path/to/foo/", "string value to post").await?;
Ok(())
}
License
Copyright 2020-2022 Jeremy Andrews
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Structs
The elements needed to build an individual user state on a Gaggle Worker.
Object created by log_debug()
and written
to log to assist in debugging.
Defines the HTTP requests that Goose makes.
Used to build a GooseRequest
object, necessary to make a request with Goose.
The response to a GooseRequestMetric
.
An individual user state, repeatedly running all Transaction
s
in a specific Scenario
.
An individual scenario.
An individual transaction within a Scenario
.
Enums
Supported HTTP methods.
Commands sent from the parent thread to the user threads, and from the manager to the worker processes.
An enumeration of all errors a Transaction
can return.
Traits
Functions
A helper to determine which host should be prepended to relative load test paths in this Scenario.
Type Definitions
The function type of a goose transaction function.
Goose transactions return a result, which is empty on success, or contains a
TransactionError
on error.