Skip to content

Commit

Permalink
Merge pull request #9 from PierreZ/buggify_crate
Browse files Browse the repository at this point in the history
Buggify crate
  • Loading branch information
PierreZ authored May 17, 2022
2 parents dddf12f + db6ec55 commit 810e1a3
Show file tree
Hide file tree
Showing 14 changed files with 203 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
rust: [nightly, beta, stable, 1.51.0]
rust: [nightly, beta, stable, 1.56.0]
steps:
- uses: actions/checkout@v2

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["circus-simulation", "circus-test"]
members = ["circus-simulation", "circus-test", "circus-buggify"]
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A toolkit to develop distributed systems
[![Coverage Status](https://coveralls.io/repos/github/PierreZ/circus/badge.svg?branch=main)](https://coveralls.io/github/PierreZ/circus?branch=main)
[![Dependency Status](https://deps.rs/repo/github/PierreZ/circus/status.svg)](https://deps.rs/repo/github/PierreZ/circus)
![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.51.0+-lightgray.svg)](#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56.0+-lightgray.svg)](#rust-version-requirements)

## Overview

Expand All @@ -19,12 +19,13 @@ It will be compatible with both `async-std` and `Tokio`, allowing you to use Cir

## Available crates

* [circus-simulation](/circus-simulation) [![Crates.io Version](https://img.shields.io/crates/v/circus_simulation.svg)](https://crates.io/crates/circus_simulation) [![Docs.rs](https://img.shields.io/docsrs/circus_simulation)](https://docs.rs/circus_simulation)
* [circus-test](/circus-test) [![Crates.io Version](https://img.shields.io/crates/v/circus_test.svg)](https://crates.io/crates/circus_test) [![Docs.rs](https://img.shields.io/docsrs/circus_test)](https://docs.rs/circus_test)
* [circus-buggify](/circus-buggify) [![Crates.io Version](https://img.shields.io/crates/v/circus_buggify.svg)](https://crates.io/crates/circus_buggify) [![Docs.rs](https://img.shields.io/docsrs/circus_buggify)](https://docs.rs/circus_buggify)
* [circus-simulation](/circus-simulation) [![Crates.io Version](https://img.shields.io/crates/v/circus_simulation.svg)](https://crates.io/crates/circus_simulation) [![Docs.rs](https://img.shields.io/docsrs/circus_simulation)](https://docs.rs/circus_simulation)

## Rust version requirements

The MSRV is Rust 1.51.0.
The MSRV is Rust 1.56.0.

## Examples

Expand Down
38 changes: 38 additions & 0 deletions circus-buggify/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "circus_buggify"
version = "0.1.0"
authors = ["Pierre Zemb <[email protected]>"]
edition = "2021"
rust-version = "1.56"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/PierreZ/circus"
homepage = "https://github.com/PierreZ/circus"
documentation = "https://docs.rs/circus_buggify"

include = [
"../LICENSE-APACHE",
"../LICENSE-MIT",
"README.md",
".gitignore",
"Cargo.toml",
"src/*.rs",
"src/*/*.rs",
"tests/*.rs",
]

description = "A Rust port of the Buggify macro from FoundationDB"
categories = ["simulation"]

[dependencies]
rand = { version = "0.8.5", features = ["small_rng"] }
parking_lot = "0.12.0"
once_cell = "1.10.0"

[dev-dependencies]
tracing = "0.1.34"
tracing-subscriber = "0.3.11"

[badges]
coveralls = { repository = "PierreZ/circus", branch = "main", service = "github" }
maintenance = { status = "experimental" }
33 changes: 33 additions & 0 deletions circus-buggify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# circus-simulation :circus_tent:

A simulation framework, inspired by [FoundationDB](https://foundationdb.org)

![status](https://img.shields.io/badge/status-experimental-red)
[![Crates.io Version](https://img.shields.io/crates/v/circus_simulation.svg)](https://crates.io/crates/circus_simulation)
[![Docs.rs](https://img.shields.io/docsrs/circus_simulation)](https://docs.rs/circus_simulation)
[![Build status](https://github.com/PierreZ/circus/workflows/Build%20and%20test/badge.svg)](https://github.com/PierreZ/circus/actions)
![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56.0+-lightgray.svg)](#rust-version-requirements)

## Rust version requirements

The MSRV is Rust 1.56.0.

## Examples

Examples can be found in the [examples folder](/circus-simulation/examples).

## License

Licensed under either of

* Apache License, Version 2.0 ([LICENSE-APACHE](/LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](/LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
extern crate circus_simulation;
extern crate circus_buggify;

use circus_simulation::buggify::{buggifier, Buggifier};
use circus_simulation::deterministic::random::DeterministicRandom;
use circus_buggify::{buggify_with_prob, enable_buggify, Buggifier};
use rand::rngs::SmallRng;
use rand::SeedableRng;
use tracing::Level;

fn main() {
Expand All @@ -12,11 +13,8 @@ fn main() {
// let's create a buggifier
let b = Buggifier::default();

// init random with a seed
let random = DeterministicRandom::new_with_seed(42);

// enables buggify
b.enable_buggify(random);
// enables buggify with a seed
b.enable_buggify(SmallRng::seed_from_u64(42));

for i in 0..10 {
// this block has a 0.05% chance to be run
Expand All @@ -32,8 +30,8 @@ fn main() {
}

// you can also get a static buggifier that needs to be enabled
buggifier().enable_buggify(DeterministicRandom::new_with_seed(42));
if buggifier().buggify_with_prob(1.00) {
enable_buggify(SmallRng::seed_from_u64(42));
if buggify_with_prob(1.00) {
tracing::info!("buggified with a 100% probability!");
}
}
124 changes: 96 additions & 28 deletions circus-simulation/src/buggify.rs → circus-buggify/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
//! Inject failure with buggify
use crate::deterministic::random::DeterministicRandom;
//! `buggify` allow you to cooperate with the simulator to inject failures.
//! It has the following rules:
//! 1. it only ever evaluates to true when run in simulation.
//! 1. The first time each `buggify` use is evaluated, it is either enabled or disabled for the entire simulation run.
//! 1. Enabled uses of `buggify` have a 5% chance of evaluating to true
//!
//! A good blogpost about buggify can be found [here](https://transactional.blog/simulation/buggify.html).
//! ```rust
//! use circus_buggify::{buggify_with_prob, enable_buggify, Buggifier};
//! use rand::rngs::SmallRng;
//! use rand::SeedableRng;
//!
//! // let's create a buggifier
//! let b = Buggifier::default();
//!
//! // enables buggify with a seed
//! b.enable_buggify(SmallRng::seed_from_u64(42));
//!
//! for i in 0..10 {
//! // this block has a 0.05% chance to be run
//! // which is iteration 8 for seed 42
//! if b.buggify() {
//! println!("buggified at iteration {}", i);
//! }
//! }
//!
//! // buggify can also accept a probability
//! if b.buggify_with_prob(1.0) {
//! println!("buggified with a 100% probability!");
//! }
//!
//! // you can also get a static buggifier that needs to be enabled
//! enable_buggify(SmallRng::seed_from_u64(42));
//! if buggify_with_prob(1.00) {
//! println!("buggified with a 100% probability!");
//! }
//!```
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
use parking_lot::Mutex;
use std::collections::HashMap;

use once_cell::sync::Lazy;
use rand::rngs::SmallRng;
use rand::Rng;
use std::ops::Deref;
use std::panic::Location;

/// Buggifier is providing the buggify methods.
/// `buggify` allow you to cooperate with the simulator to inject failures.
/// It has the following rules:
/// 1. it only ever evaluates to true when run in simulation.
/// 1. The first time each `buggify` use is evaluated, it is either enabled or disabled for the entire simulation run.
/// 1. Enabled uses of `buggify` have a 5% chance of evaluating to true
///
/// A good blogpost about buggify can be found [here](https://transactional.blog/simulation/buggify.html).
///
/// Buggifier's definition
#[derive(Debug)]
pub struct Buggifier {
buggified_lines: Mutex<HashMap<String, bool>>,
random: Mutex<Option<DeterministicRandom>>,
random: Mutex<Option<SmallRng>>,
}

impl Buggifier {
/// create a new Buggifier
pub fn new(r: DeterministicRandom) -> Self {
pub fn new(r: SmallRng) -> Self {
Buggifier {
buggified_lines: Mutex::new(HashMap::new()),
random: Mutex::new(Some(r)),
Expand Down Expand Up @@ -55,9 +88,8 @@ impl Buggifier {
Some(deterministic_random) => {
let mut already_buggified = self.buggified_lines.lock();
if !already_buggified.contains_key(&line)
&& deterministic_random.random_boolean(probability)
&& deterministic_random.gen_bool(probability)
{
tracing::info!("buggifying line {}", line);
already_buggified.insert(line, true);
return true;
}
Expand All @@ -72,15 +104,13 @@ impl Buggifier {
}

/// enables buggify by giving a random source
pub fn enable_buggify(&self, r: DeterministicRandom) {
tracing::info!("enabling buggify");
pub fn enable_buggify(&self, r: SmallRng) {
let mut data = self.random.lock();
*data = Some(r);
}

/// disable buggify
pub fn disable_buggify(&self) {
tracing::info!("disabling buggify");
let mut data = self.random.lock();
*data = None;
let mut map = self.buggified_lines.lock();
Expand All @@ -107,14 +137,50 @@ pub fn buggifier() -> &'static Buggifier {
BUGGIFIER_INSTANCE.deref()
}

#[track_caller]
/// `buggify` will returns true only once per execution with a probability of 0.05.
pub fn buggify() -> bool {
let location = Location::caller();
buggifier().handle_buggify(format!("{}:{}", location.file(), location.line()), 0.05)
}

#[track_caller]
/// `buggify` version where you can choose the probability.
pub fn buggify_with_prob(probability: f64) -> bool {
let location = Location::caller();
buggifier().handle_buggify(
format!("{}:{}", location.file(), location.line()),
probability,
)
}

/// checks if buggify is enabled
pub fn is_buggify_enabled() -> bool {
buggifier().is_buggify_enabled()
}

/// enables buggify by giving a random source
pub fn enable_buggify(r: SmallRng) {
buggifier().enable_buggify(r)
}

/// disable buggify
pub fn disable_buggify() {
buggifier().disable_buggify()
}

#[cfg(test)]
mod tests {
use crate::buggify::{buggifier, Buggifier};
use crate::deterministic::random::DeterministicRandom;
use crate::{
buggifier, buggify, buggify_with_prob, disable_buggify, enable_buggify, is_buggify_enabled,
Buggifier,
};
use rand::rngs::SmallRng;
use rand::SeedableRng;
use tracing::Level;

#[test]
fn buggify() {
fn test_buggifier() {
let _ = tracing_subscriber::fmt()
.with_max_level(Level::TRACE)
.with_test_writer()
Expand All @@ -132,7 +198,7 @@ mod tests {
assert!((*map).is_empty());
}

let random = DeterministicRandom::new_with_seed(42);
let random = SmallRng::seed_from_u64(42);
b.enable_buggify(random);
assert!(b.is_buggify_enabled(), "should be activated");

Expand Down Expand Up @@ -166,23 +232,25 @@ mod tests {
}

#[test]
fn static_buggify() {
fn test_static_buggify() {
let _ = tracing_subscriber::fmt()
.with_max_level(Level::TRACE)
.with_test_writer()
.try_init();

assert!(!buggifier().is_buggify_enabled());
assert!(!buggifier().buggify_with_prob(1.0), "should not buggified");
// reset any previous test
disable_buggify();

assert!(!is_buggify_enabled());
assert!(!buggify_with_prob(1.0), "should not buggified");

let random = DeterministicRandom::new_with_seed(42);
buggifier().enable_buggify(random);
assert!(buggifier().is_buggify_enabled(), "should be activated");
enable_buggify(SmallRng::seed_from_u64(42));
assert!(is_buggify_enabled(), "should be activated");

for i in 0..100 {
let result = i == 8;
assert_eq!(
buggifier().buggify(),
buggify(),
result,
"iteration {} should have been {}",
i,
Expand Down
6 changes: 4 additions & 2 deletions circus-simulation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[package]
name = "circus_simulation"
version = "0.0.1"
version = "0.1.0"
authors = ["Pierre Zemb <[email protected]>"]
edition = "2018"
edition = "2021"
rust-version = "1.56"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/PierreZ/circus"
Expand All @@ -24,6 +25,7 @@ description = "Simulation framework inspired by FoundationDB"
categories = ["simulation"]

[dependencies]
circus_buggify = { version = "0.1.0", path = "../circus-buggify"}
rand = { version = "0.8.5", features = ["small_rng"] }
parking_lot = "0.12.0"
once_cell = "1.10.0"
Expand Down
14 changes: 7 additions & 7 deletions circus-simulation/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# circus-simulation :circus_tent:
# circus-buggify :circus_tent:

A simulation framework, inspired by [FoundationDB](https://foundationdb.org)
A buggify-like feature, inspired by [FoundationDB](https://foundationdb.org)

![status](https://img.shields.io/badge/status-experimental-red)
[![Crates.io Version](https://img.shields.io/crates/v/circus_simulation.svg)](https://crates.io/crates/circus_simulation)
[![Docs.rs](https://img.shields.io/docsrs/circus_simulation)](https://docs.rs/circus_simulation)
[![Crates.io Version](https://img.shields.io/crates/v/circus_buggify.svg)](https://crates.io/crates/circus_simulation)
[![Docs.rs](https://img.shields.io/docsrs/circus_buggify)](https://docs.rs/circus_simulation)
[![Build status](https://github.com/PierreZ/circus/workflows/Build%20and%20test/badge.svg)](https://github.com/PierreZ/circus/actions)
![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.51.0+-lightgray.svg)](#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56.0+-lightgray.svg)](#rust-version-requirements)

## Rust version requirements

The MSRV is Rust 1.51.0.
The MSRV is Rust 1.56.0.

## Examples

Examples can be found in the [examples folder](/circus-simulation/examples).
Examples can be found in the [examples folder](/circus-buggify/examples).

## License

Expand Down
Loading

0 comments on commit 810e1a3

Please sign in to comment.