Skip to content

Commit

Permalink
Add docs for the builder and config focused on defaults (#152)
Browse files Browse the repository at this point in the history
Add documentation to the sim builder and the config, particularly
focused on default values.

Also fix a few errors that emerged from running `cargo doc`, and make
`build_with_rng` public again (mistake in #150).
  • Loading branch information
Benjscho authored Nov 17, 2023
1 parent 3584b22 commit b8ce7d1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 7 deletions.
80 changes: 77 additions & 3 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,67 @@ use crate::*;
use rand::{RngCore, SeedableRng};
use std::time::{Duration, SystemTime};

/// Configure the simulation
/// A builder that can be used to configure the simulation.
///
/// The Builder allows you to set a number of options when creating a turmoil
/// simulation, see the available methods for documentation of each of these
/// options.
///
/// ## Examples
///
/// You can use the builder to initialize a sim with default configuration:
///
/// ```
/// let sim = turmoil::Builder::new().build();
/// ```
///
/// If you want to vary factors of the simulation, you can use the
/// respective Builder methods:
///
/// ```
/// use std::time::{Duration, SystemTime};
///
/// let sim = turmoil::Builder::new()
/// .simulation_duration(Duration::from_secs(60))
/// .epoch(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(946684800)).unwrap())
/// .fail_rate(0.05) // 5% failure rate
/// .build();
/// ```
///
/// If you create a builder with a set of options you can then repeatedly
/// call `build` to get a sim with the same settings:
///
/// ```
/// use std::time::Duration;
///
/// // Create a persistent builder
/// let mut builder = turmoil::Builder::new();
///
/// // Apply a chain of options to that builder
/// builder.simulation_duration(Duration::from_secs(45))
/// .fail_rate(0.05);
///
/// let sim_one = builder.build();
/// let sim_two = builder.build();
/// ````
///
/// ## Entropy and randomness
///
/// By default, the builder will use its own `rng` to determine the variation
/// in random factors that affect the simulation, like message latency, and
/// failure distributions. To make your tests deterministic, you can use your
/// own seeded `rng` provider when building the simulation through
/// `build_with_rng`.
///
/// For example:
///
/// ```
/// use rand::rngs::SmallRng;
/// use rand::SeedableRng;
///
/// let rng = SmallRng::seed_from_u64(0);
/// let sim = turmoil::Builder::new().build_with_rng(Box::new(rng));
/// ```
pub struct Builder {
config: Config,

Expand Down Expand Up @@ -54,45 +114,59 @@ impl Builder {
self
}

/// The minimum latency that a message will take to transfer over a
/// link on the network.
pub fn min_message_latency(&mut self, value: Duration) -> &mut Self {
self.link.latency_mut().min_message_latency = value;
self
}

/// The maximum latency that a message will take to transfer over a
/// link on the network.
pub fn max_message_latency(&mut self, value: Duration) -> &mut Self {
self.link.latency_mut().max_message_latency = value;
self
}

/// The failure rate of messages on the network. For TCP
/// this will break connections as currently there are
/// no re-send capabilities. With UDP this is useful for
/// testing network flakiness.
pub fn fail_rate(&mut self, value: f64) -> &mut Self {
self.link.message_loss_mut().fail_rate = value;
self
}

/// The repair rate of messages on the network. This is how
/// frequently a link is repaired after breaking.
pub fn repair_rate(&mut self, value: f64) -> &mut Self {
self.link.message_loss_mut().repair_rate = value;
self
}

/// Capacity of a host's TCP buffer in the sim.
pub fn tcp_capacity(&mut self, value: usize) -> &mut Self {
self.config.tcp_capacity = value;
self
}

/// Capacity of host's UDP buffer in the sim.
pub fn udp_capacity(&mut self, value: usize) -> &mut Self {
self.config.udp_capacity = value;
self
}

/// Build with default rng.
/// Build a simulation with the settings from the builder.
///
/// This will use default rng with entropy from the device running.
pub fn build<'a>(&self) -> Sim<'a> {
self.build_with_rng(Box::new(rand::rngs::SmallRng::from_entropy()))
}

/// Build a sim with a provided `rng`.
///
/// This allows setting the random number generator used to fuzz
fn build_with_rng<'a>(&self, rng: Box<dyn RngCore>) -> Sim<'a> {
pub fn build_with_rng<'a>(&self, rng: Box<dyn RngCore>) -> Sim<'a> {
if self.link.latency().max_message_latency < self.link.latency().min_message_latency {
panic!("Maximum message latency must be greater than minimum.");
}
Expand Down
22 changes: 21 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
use rand_distr::Exp;
use std::time::{Duration, SystemTime};

/// Configuration for a simulation.
///
/// This configuration allows you to define different aspects of how the
/// simulation should run, such as the duration, or the tick rate.
///
/// ## Default values
///
/// The config provides a default of:
/// - duration: 10 seconds
/// - tick: 1ms
/// - epoch: the current system time
/// - tcp_capacity: 64
/// - udp_capacity: 64
#[derive(Clone)]
pub(crate) struct Config {
/// How long the test should run for in simulated time.
Expand Down Expand Up @@ -30,6 +43,11 @@ pub(crate) struct Link {
}

/// Configure latency behavior between two hosts.
///
/// Provides default values of:
/// - min_message_latency: 0ms
/// - max_message_latency: 100ms
/// - latency_distribution: Exp(5)
#[derive(Clone)]
pub(crate) struct Latency {
/// Minimum latency
Expand All @@ -42,7 +60,9 @@ pub(crate) struct Latency {
pub(crate) latency_distribution: Exp<f64>,
}

/// Configure how often messages are lost
/// Configure how often messages are lost.
///
/// Provides default values of 0% failure rate, and 100% repair rate.
#[derive(Clone)]
pub(crate) struct MessageLoss {
/// Probability of a link failing
Expand Down
2 changes: 1 addition & 1 deletion src/net/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ impl UdpSocket {
/// # Cancel safety
///
/// This method is cancel safe. If `send_to` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// [`tokio::select!`](tokio::select) statement and some other branch
/// completes first, then it is guaranteed that the message was not sent.
pub async fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], target: A) -> Result<usize> {
World::current(|world| {
Expand Down
4 changes: 2 additions & 2 deletions src/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl<'a> Sim<'a> {
.map(|h| h.to_owned())
}

/// Hold messages between two hosts, or sets of hosts, until [`release`] is
/// Hold messages between two hosts, or sets of hosts, until [`release`](crate::release) is
/// called.
pub fn hold(&self, a: impl ToIpAddrs, b: impl ToIpAddrs) {
let mut world = self.world.borrow_mut();
Expand All @@ -193,7 +193,7 @@ impl<'a> Sim<'a> {
world.repair_many(a, b);
}

/// The opposite of [`hold`]. All held messages are immediately delivered.
/// The opposite of [`hold`](crate::hold). All held messages are immediately delivered.
pub fn release(&self, a: impl ToIpAddrs, b: impl ToIpAddrs) {
let mut world = self.world.borrow_mut();
world.release_many(a, b);
Expand Down

0 comments on commit b8ce7d1

Please sign in to comment.