thesis/
lib.rs

1//! Thesis provides the `Experiment` struct, which represents an experiment to
2//! run which compares the return values of multiple methods for accomplishing
3//! the same task.
4//!
5//! Let's imagine that we already have a function called `load_data_from_db`,
6//! which loads some data from a database. We want to refactor this to instead
7//! load the same data from redis. We write a new function called
8//! `load_data_from_redis` to accomplish the same task, but with redis instead of
9//! a DB. We want to use the redis version on only a very small percentage of
10//! traffic, say 0.5% of incoming requests, and we want to log it out if the
11//! redis data doesn't match the DB data, treating the DB data as accurate and
12//! discarding the redis data if so. Here's how we can use an `Experiment` to do
13//! this.
14//!
15//! ```
16//! use thesis::{Experiment, rollout::Percent};
17//!
18//! async fn load_data_from_db(id: i32) -> i32 { id }
19//! async fn load_data_from_redis(id: i32) -> i32 { id }
20//!
21//! # tokio_test::block_on(async {
22//! let id = 4;
23//! let result = Experiment::new("redis migration")
24//!     .control(load_data_from_db(id))
25//!     .experimental(load_data_from_redis(id))
26//!     .rollout_strategy(Percent::new(0.5))
27//!     .on_mismatch(|mismatch| {
28//!         eprintln!(
29//!             "DB & Redis data differ - db={}, redis={}",
30//!             mismatch.control,
31//!             mismatch.experimental,
32//!         );
33//!
34//!         // the `control` value here comes from the DB
35//!         mismatch.control
36//!     })
37//!     .run()
38//!     .await;
39//!
40//! assert_eq!(result, 4);
41//! # });
42//! ```
43
44pub mod experiment;
45pub mod mismatch;
46pub mod rollout;
47
48pub use experiment::Experiment;
49pub use mismatch::{Mismatch, MismatchHandler};
50pub use rollout::{RolloutDecision, RolloutStrategy};