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};