wb_cache/test/
simulation.rs

1//! This module serves a few purposes:
2//!
3//! 1. Test to verify various aspects of the `wb-cache` crate functionality.
4//! 2. Benchmark to run exactly the same scenario with and without caching.
5//! 3. Serve as a sample implementation of [data controllers](crate#data-controller) and user
6//!    code using caching in a semi-realistic setting.
7//!
8//! Since, one way or another, everything revolves around the simulation, this term is used most frequently in all
9//! references to this module.
10//!
11//! # Introduction
12//!
13//! The big challenge of benchmarking is to produce results that closely resemble real-life applications. This
14//! simulation attempts to solve that problem by imitating an e-commerce startup.
15//!
16//! Here is the business plan:
17//!
18//! 1. Offer a number of products for sale.
19//! 2. For each product, have a supplier with parameters such as reliability and shipping time.
20//! 3. Start with some or no customers.
21//! 4. Allow the number of customers to grow each day, up to a market capacity that cannot be exceeded.
22//! 5. Initially, the customer base grows slowly; until an inflection point is reached when growth increases; after the
23//!    point, the growth slows down gradually.
24//! 6. Each customer purchasing habits may vary with some being more regular and others more erratic.
25//! 7. The number of units sold per day for a product is a function of its popularity and price.
26//! 8. For simplicity, each order consists of a single product.
27//! 9. Due to irregularities in shipments, it is possible to run out of stock. In that case, orders for that product
28//!    will remain pending until the inventory is replenished (backordering).
29//! 10. Each simulation step corresponds to a single day.
30//!
31//! To achieve near-realistic parameters, various processes are simulated using random distributions that best capture
32//! the intended characteristics.
33//!
34//! # Components
35//!
36//! The simulation consists of a few major components:
37//!
38//! - The [application](sim_app::EcommerceApp) responsible for setting up the environment and binding the other
39//!   components together.
40//! - [Backend database support](db).
41//! - A scriptwriter that creates a step-by-step scenario describing the events that affect the database content.
42//! - Actors that execute the screenplay. Each simulation run involves two actors performing simultaneously: one
43//!   implements the non-cached (plain) mechanics, and the other the cached version.
44//!
45//! # How It Works
46//!
47//! To achieve stable results, the simulation is implemented as follows:
48//!
49//! First, a scenario is generated. It is either created randomly by the [scriptwriter](scriptwriter::ScriptWriter)
50//! or loaded from a file where it was saved after a previous run. Saving and loading scenarios can be used to ensure
51//! that exactly the same benchmark is run each time.
52//!
53//! The scenario is then executed by the [actors](actor::TestActor), as mentioned above. To mitigate the influence
54//! of various performance-affecting factors, the actors are run in parallel. However, it must be kept in mind that
55//! the caching actor normally finishes much earlier than the plain actor.
56//!
57//! If the [`sim_app::EcommerceApp`] receives the `--test` command line argument, it will compare
58//! the results of the two actors by matching the database content on a record-by-record basis. If discrepancies are
59//! found, the simulation will fail with an error message indicating the first mismatch.
60//!
61//! # Running the Simulation
62//!
63//! To run the simulation, execute one of the following commands:
64//!
65//! ```shell
66//! cargo run --profile release --feature sqlite --example company -- --sqlite --test
67//! ```
68//!
69//! or
70//!
71//! ```shell
72//! cargo run --profile release --features pg --example company -- --pg --test
73//! ```
74//!
75//! _**Note:** The release profile is optional but speeds up the caching code, yielding better benchmarking results._
76//!
77//! Running with the PostgreSQL driver requires connection parameters to be provided. This can be done either via the
78//! command line or environment variables. To see the available options, run:
79//!
80//! ```shell
81//! cargo run --features pg --example company -- --help
82//! ```
83//!
84//! or, to get full help:
85//!
86//! ```shell
87//! cargo run --all-features --example company -- --help
88//! ```
89pub mod actor;
90pub mod company_cached;
91pub mod company_plain;
92pub mod db;
93pub mod progress;
94pub mod scriptwriter;
95pub mod sim_app;
96pub mod types;
97
98use std::fmt::Debug;
99
100use indicatif::ProgressBar;
101use progress::POrder;
102use progress::PStyle;
103use types::simerr;
104use types::SimErrorAny;
105
106pub trait SimulationApp: Debug + Sync + Send + 'static {
107    fn acquire_progress<'a>(
108        &'a self,
109        style: PStyle,
110        order: Option<POrder<'a>>,
111    ) -> Result<Option<ProgressBar>, SimErrorAny>;
112
113    fn app_is_gone() -> SimErrorAny {
114        simerr!("App is gone")
115    }
116    fn set_cached_per_sec(&self, step: f64);
117    fn set_plain_per_sec(&self, step: f64);
118
119    fn report_debug<S: ToString>(&self, msg: S);
120    fn report_info<S: ToString>(&self, msg: S);
121    fn report_warn<S: ToString>(&self, msg: S);
122    fn report_error<S: ToString>(&self, msg: S);
123}