From 9f08b621ec9c0277a42e4c2a269b6bd16300b57f Mon Sep 17 00:00:00 2001 From: alpaylan Date: Sun, 22 Dec 2024 17:06:46 -0500 Subject: [PATCH 1/4] add clap CLI for configuring the simulator --- Cargo.lock | 1 + simulator/Cargo.toml | 1 + simulator/main.rs | 80 ++++++++++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53b97bfc1..e654e1bf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1164,6 +1164,7 @@ name = "limbo_sim" version = "0.0.10" dependencies = [ "anarchist-readable-name-generator-lib", + "clap", "env_logger 0.10.2", "limbo_core", "log", diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index 51351aee2..31a54f1e6 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -22,3 +22,4 @@ log = "0.4.20" tempfile = "3.0.7" env_logger = "0.10.1" anarchist-readable-name-generator-lib = "0.1.2" +clap = { version = "4.5", features = ["derive"] } diff --git a/simulator/main.rs b/simulator/main.rs index 085711391..154fa76e2 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -1,3 +1,4 @@ +use clap::{command, Parser}; use generation::plan::{Interaction, InteractionPlan, ResultSet}; use generation::{pick, pick_index, Arbitrary, ArbitraryFrom}; use limbo_core::{Connection, Database, File, OpenFlags, PlatformIO, Result, RowResult, IO}; @@ -8,8 +9,11 @@ use rand::prelude::*; use rand_chacha::ChaCha8Rng; use std::cell::RefCell; use std::io::Write; +use std::panic::UnwindSafe; +use std::path::Path; use std::rc::Rc; use std::sync::Arc; +use std::time::Duration; use tempfile::TempDir; mod generation; @@ -25,6 +29,8 @@ struct SimulatorEnv { rng: ChaCha8Rng, } +impl UnwindSafe for SimulatorEnv {} + #[derive(Clone)] enum SimConnection { Connected(Rc), @@ -45,14 +51,48 @@ struct SimulatorOpts { page_size: usize, } +#[derive(Parser)] +#[command(name = "limbo-simulator")] +#[command(author, version, about, long_about = None)] +pub struct SimulatorCLI { + #[clap(short, long, help = "set seed for reproducible runs", default_value = None)] + pub seed: Option, + #[clap(short, long, help = "set custom output directory for produced files", default_value = None)] + pub output_dir: Option, + #[clap( + short, + long, + help = "enable doublechecking, run the simulator with the plan twice and check output equality" + )] + pub doublecheck: bool, + #[clap(short, long, help = "change the maximum size of the randomly generated sequence of interactions", default_value_t = 1024)] + pub maximum_size: usize, +} + #[allow(clippy::arc_with_non_send_sync)] fn main() { let _ = env_logger::try_init(); - let seed = match std::env::var("SEED") { - Ok(seed) => seed.parse::().unwrap(), - Err(_) => rand::thread_rng().next_u64(), + + let opts = SimulatorCLI::parse(); + + let seed = match opts.seed { + Some(seed) => seed, + None => rand::thread_rng().next_u64(), }; - println!("Seed: {}", seed); + + let output_dir = match opts.output_dir { + Some(dir) => Path::new(&dir).to_path_buf(), + None => TempDir::new().unwrap().into_path(), + }; + + let db_path = output_dir.join("simulator.db"); + let plan_path = output_dir.join("simulator.plan"); + + // Print the seed, the locations of the database and the plan file + log::info!("database path: {:?}", db_path); + log::info!("simulator plan path: {:?}", plan_path); + log::info!("seed: {}", seed); + let mut rng = ChaCha8Rng::seed_from_u64(seed); let (read_percent, write_percent, delete_percent) = { @@ -66,7 +106,7 @@ fn main() { }; let opts = SimulatorOpts { - ticks: rng.gen_range(0..10240), + ticks: rng.gen_range(0..opts.maximum_size), max_connections: 1, // TODO: for now let's use one connection as we didn't implement // correct transactions procesing max_tables: rng.gen_range(0..128), @@ -74,21 +114,19 @@ fn main() { write_percent, delete_percent, page_size: 4096, // TODO: randomize this too - max_interactions: rng.gen_range(0..10240), + max_interactions: rng.gen_range(0..opts.maximum_size), }; let io = Arc::new(SimulatorIO::new(seed, opts.page_size).unwrap()); - let mut path = TempDir::new().unwrap().into_path(); - path.push("simulator.db"); - println!("path to db '{:?}'", path); - let db = match Database::open_file(io.clone(), path.as_path().to_str().unwrap()) { + let db = match Database::open_file(io.clone(), db_path.to_str().unwrap()) { Ok(db) => db, Err(e) => { - panic!("error opening simulator test file {:?}: {:?}", path, e); + panic!("error opening simulator test file {:?}: {:?}", db_path, e); } }; let connections = vec![SimConnection::Disconnected; opts.max_connections]; + let mut env = SimulatorEnv { opts, tables: Vec::new(), @@ -98,30 +136,30 @@ fn main() { db, }; - println!("Initial opts {:?}", env.opts); - log::info!("Generating database interaction plan..."); let mut plans = (1..=env.opts.max_connections) .map(|_| InteractionPlan::arbitrary_from(&mut env.rng.clone(), &env)) .collect::>(); + let mut f = std::fs::File::create(plan_path.clone()).unwrap(); + // todo: create a detailed plan file with all the plans. for now, we only use 1 connection, so it's safe to use the first plan. + f.write(plans[0].to_string().as_bytes()).unwrap(); + log::info!("{}", plans[0].stats()); log::info!("Executing database interaction plan..."); - let result = execute_plans(&mut env, &mut plans); + let result = execute_plans(&mut env, &mut plans); if result.is_err() { log::error!("error executing plans: {:?}", result.err()); } - log::info!("db is at {:?}", path); - let mut path = TempDir::new().unwrap().into_path(); - path.push("simulator.plan"); - let mut f = std::fs::File::create(path.clone()).unwrap(); - f.write(plans[0].to_string().as_bytes()).unwrap(); - log::info!("plan saved at {:?}", path); - log::info!("seed was {}", seed); env.io.print_stats(); + + // Print the seed, the locations of the database and the plan file at the end again for easily accessing them. + log::info!("database path: {:?}", db_path); + log::info!("simulator plan path: {:?}", plan_path); + log::info!("seed: {}", seed); } fn execute_plans(env: &mut SimulatorEnv, plans: &mut Vec) -> Result<()> { From 833c75080bcba100ca2007868ea944e76d1ec65e Mon Sep 17 00:00:00 2001 From: alpaylan Date: Sun, 22 Dec 2024 17:16:50 -0500 Subject: [PATCH 2/4] break up the simulator primitives into their own files in the simulator submodule --- simulator/main.rs | 238 ++---------------------------------- simulator/simulator/cli.rs | 24 ++++ simulator/simulator/env.rs | 38 ++++++ simulator/simulator/file.rs | 93 ++++++++++++++ simulator/simulator/io.rs | 90 ++++++++++++++ simulator/simulator/mod.rs | 4 + 6 files changed, 257 insertions(+), 230 deletions(-) create mode 100644 simulator/simulator/cli.rs create mode 100644 simulator/simulator/env.rs create mode 100644 simulator/simulator/file.rs create mode 100644 simulator/simulator/io.rs create mode 100644 simulator/simulator/mod.rs diff --git a/simulator/main.rs b/simulator/main.rs index 154fa76e2..6a09e4f4a 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -1,73 +1,24 @@ -use clap::{command, Parser}; +use clap::Parser; use generation::plan::{Interaction, InteractionPlan, ResultSet}; -use generation::{pick, pick_index, Arbitrary, ArbitraryFrom}; -use limbo_core::{Connection, Database, File, OpenFlags, PlatformIO, Result, RowResult, IO}; -use model::query::{Create, Insert, Predicate, Query, Select}; +use generation::{pick_index, Arbitrary, ArbitraryFrom}; +use limbo_core::{Connection, Database, Result, RowResult, IO}; +use model::query::{Create, Query}; use model::table::{Column, Name, Table, Value}; -use properties::{property_insert_select, property_select_all}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use std::cell::RefCell; +use simulator::cli::SimulatorCLI; +use simulator::env::{SimConnection, SimulatorEnv, SimulatorOpts}; +use simulator::io::SimulatorIO; use std::io::Write; -use std::panic::UnwindSafe; use std::path::Path; use std::rc::Rc; use std::sync::Arc; -use std::time::Duration; use tempfile::TempDir; mod generation; mod model; mod properties; - -struct SimulatorEnv { - opts: SimulatorOpts, - tables: Vec, - connections: Vec, - io: Arc, - db: Arc, - rng: ChaCha8Rng, -} - -impl UnwindSafe for SimulatorEnv {} - -#[derive(Clone)] -enum SimConnection { - Connected(Rc), - Disconnected, -} - -#[derive(Debug, Clone)] -struct SimulatorOpts { - ticks: usize, - max_connections: usize, - max_tables: usize, - // this next options are the distribution of workload where read_percent + write_percent + - // delete_percent == 100% - read_percent: usize, - write_percent: usize, - delete_percent: usize, - max_interactions: usize, - page_size: usize, -} - -#[derive(Parser)] -#[command(name = "limbo-simulator")] -#[command(author, version, about, long_about = None)] -pub struct SimulatorCLI { - #[clap(short, long, help = "set seed for reproducible runs", default_value = None)] - pub seed: Option, - #[clap(short, long, help = "set custom output directory for produced files", default_value = None)] - pub output_dir: Option, - #[clap( - short, - long, - help = "enable doublechecking, run the simulator with the plan twice and check output equality" - )] - pub doublecheck: bool, - #[clap(short, long, help = "change the maximum size of the randomly generated sequence of interactions", default_value_t = 1024)] - pub maximum_size: usize, -} +mod simulator; #[allow(clippy::arc_with_non_send_sync)] fn main() { @@ -340,176 +291,3 @@ fn get_all_rows( } Ok(out) } - -struct SimulatorIO { - inner: Box, - fault: RefCell, - files: RefCell>>, - rng: RefCell, - nr_run_once_faults: RefCell, - page_size: usize, -} - -impl SimulatorIO { - fn new(seed: u64, page_size: usize) -> Result { - let inner = Box::new(PlatformIO::new()?); - let fault = RefCell::new(false); - let files = RefCell::new(Vec::new()); - let rng = RefCell::new(ChaCha8Rng::seed_from_u64(seed)); - let nr_run_once_faults = RefCell::new(0); - Ok(Self { - inner, - fault, - files, - rng, - nr_run_once_faults, - page_size, - }) - } - - fn inject_fault(&self, fault: bool) { - self.fault.replace(fault); - for file in self.files.borrow().iter() { - file.inject_fault(fault); - } - } - - fn print_stats(&self) { - println!("run_once faults: {}", self.nr_run_once_faults.borrow()); - for file in self.files.borrow().iter() { - file.print_stats(); - } - } -} - -impl IO for SimulatorIO { - fn open_file( - &self, - path: &str, - flags: OpenFlags, - _direct: bool, - ) -> Result> { - let inner = self.inner.open_file(path, flags, false)?; - let file = Rc::new(SimulatorFile { - inner, - fault: RefCell::new(false), - nr_pread_faults: RefCell::new(0), - nr_pwrite_faults: RefCell::new(0), - reads: RefCell::new(0), - writes: RefCell::new(0), - syncs: RefCell::new(0), - page_size: self.page_size, - }); - self.files.borrow_mut().push(file.clone()); - Ok(file) - } - - fn run_once(&self) -> Result<()> { - if *self.fault.borrow() { - *self.nr_run_once_faults.borrow_mut() += 1; - return Err(limbo_core::LimboError::InternalError( - "Injected fault".into(), - )); - } - self.inner.run_once().unwrap(); - Ok(()) - } - - fn generate_random_number(&self) -> i64 { - self.rng.borrow_mut().next_u64() as i64 - } - - fn get_current_time(&self) -> String { - "2024-01-01 00:00:00".to_string() - } -} - -struct SimulatorFile { - inner: Rc, - fault: RefCell, - nr_pread_faults: RefCell, - nr_pwrite_faults: RefCell, - writes: RefCell, - reads: RefCell, - syncs: RefCell, - page_size: usize, -} - -impl SimulatorFile { - fn inject_fault(&self, fault: bool) { - self.fault.replace(fault); - } - - fn print_stats(&self) { - println!( - "pread faults: {}, pwrite faults: {}, reads: {}, writes: {}, syncs: {}", - *self.nr_pread_faults.borrow(), - *self.nr_pwrite_faults.borrow(), - *self.reads.borrow(), - *self.writes.borrow(), - *self.syncs.borrow(), - ); - } -} - -impl limbo_core::File for SimulatorFile { - fn lock_file(&self, exclusive: bool) -> Result<()> { - if *self.fault.borrow() { - return Err(limbo_core::LimboError::InternalError( - "Injected fault".into(), - )); - } - self.inner.lock_file(exclusive) - } - - fn unlock_file(&self) -> Result<()> { - if *self.fault.borrow() { - return Err(limbo_core::LimboError::InternalError( - "Injected fault".into(), - )); - } - self.inner.unlock_file() - } - - fn pread(&self, pos: usize, c: Rc) -> Result<()> { - if *self.fault.borrow() { - *self.nr_pread_faults.borrow_mut() += 1; - return Err(limbo_core::LimboError::InternalError( - "Injected fault".into(), - )); - } - *self.reads.borrow_mut() += 1; - self.inner.pread(pos, c) - } - - fn pwrite( - &self, - pos: usize, - buffer: Rc>, - c: Rc, - ) -> Result<()> { - if *self.fault.borrow() { - *self.nr_pwrite_faults.borrow_mut() += 1; - return Err(limbo_core::LimboError::InternalError( - "Injected fault".into(), - )); - } - *self.writes.borrow_mut() += 1; - self.inner.pwrite(pos, buffer, c) - } - - fn sync(&self, c: Rc) -> Result<()> { - *self.syncs.borrow_mut() += 1; - self.inner.sync(c) - } - - fn size(&self) -> Result { - self.inner.size() - } -} - -impl Drop for SimulatorFile { - fn drop(&mut self) { - self.inner.unlock_file().expect("Failed to unlock file"); - } -} diff --git a/simulator/simulator/cli.rs b/simulator/simulator/cli.rs new file mode 100644 index 000000000..f977937bb --- /dev/null +++ b/simulator/simulator/cli.rs @@ -0,0 +1,24 @@ +use clap::{command, Parser}; + +#[derive(Parser)] +#[command(name = "limbo-simulator")] +#[command(author, version, about, long_about = None)] +pub struct SimulatorCLI { + #[clap(short, long, help = "set seed for reproducible runs", default_value = None)] + pub seed: Option, + #[clap(short, long, help = "set custom output directory for produced files", default_value = None)] + pub output_dir: Option, + #[clap( + short, + long, + help = "enable doublechecking, run the simulator with the plan twice and check output equality" + )] + pub doublecheck: bool, + #[clap( + short, + long, + help = "change the maximum size of the randomly generated sequence of interactions", + default_value_t = 1024 + )] + pub maximum_size: usize, +} diff --git a/simulator/simulator/env.rs b/simulator/simulator/env.rs new file mode 100644 index 000000000..0107ac501 --- /dev/null +++ b/simulator/simulator/env.rs @@ -0,0 +1,38 @@ +use std::rc::Rc; +use std::sync::Arc; + +use limbo_core::{Connection, Database}; +use rand_chacha::ChaCha8Rng; + +use crate::model::table::Table; + +use crate::simulator::io::SimulatorIO; + +pub(crate) struct SimulatorEnv { + pub(crate) opts: SimulatorOpts, + pub(crate) tables: Vec
, + pub(crate) connections: Vec, + pub(crate) io: Arc, + pub(crate) db: Arc, + pub(crate) rng: ChaCha8Rng, +} + +#[derive(Clone)] +pub(crate) enum SimConnection { + Connected(Rc), + Disconnected, +} + +#[derive(Debug, Clone)] +pub(crate) struct SimulatorOpts { + pub(crate) ticks: usize, + pub(crate) max_connections: usize, + pub(crate) max_tables: usize, + // this next options are the distribution of workload where read_percent + write_percent + + // delete_percent == 100% + pub(crate) read_percent: usize, + pub(crate) write_percent: usize, + pub(crate) delete_percent: usize, + pub(crate) max_interactions: usize, + pub(crate) page_size: usize, +} diff --git a/simulator/simulator/file.rs b/simulator/simulator/file.rs new file mode 100644 index 000000000..7f3fe9072 --- /dev/null +++ b/simulator/simulator/file.rs @@ -0,0 +1,93 @@ +use std::{cell::RefCell, rc::Rc}; + +use limbo_core::{File, Result}; + +pub(crate) struct SimulatorFile { + pub(crate) inner: Rc, + pub(crate) fault: RefCell, + pub(crate) nr_pread_faults: RefCell, + pub(crate) nr_pwrite_faults: RefCell, + pub(crate) writes: RefCell, + pub(crate) reads: RefCell, + pub(crate) syncs: RefCell, + pub(crate) page_size: usize, +} + +impl SimulatorFile { + pub(crate) fn inject_fault(&self, fault: bool) { + self.fault.replace(fault); + } + + pub(crate) fn print_stats(&self) { + println!( + "pread faults: {}, pwrite faults: {}, reads: {}, writes: {}, syncs: {}", + *self.nr_pread_faults.borrow(), + *self.nr_pwrite_faults.borrow(), + *self.reads.borrow(), + *self.writes.borrow(), + *self.syncs.borrow(), + ); + } +} + +impl limbo_core::File for SimulatorFile { + fn lock_file(&self, exclusive: bool) -> Result<()> { + if *self.fault.borrow() { + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); + } + self.inner.lock_file(exclusive) + } + + fn unlock_file(&self) -> Result<()> { + if *self.fault.borrow() { + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); + } + self.inner.unlock_file() + } + + fn pread(&self, pos: usize, c: Rc) -> Result<()> { + if *self.fault.borrow() { + *self.nr_pread_faults.borrow_mut() += 1; + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); + } + *self.reads.borrow_mut() += 1; + self.inner.pread(pos, c) + } + + fn pwrite( + &self, + pos: usize, + buffer: Rc>, + c: Rc, + ) -> Result<()> { + if *self.fault.borrow() { + *self.nr_pwrite_faults.borrow_mut() += 1; + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); + } + *self.writes.borrow_mut() += 1; + self.inner.pwrite(pos, buffer, c) + } + + fn sync(&self, c: Rc) -> Result<()> { + *self.syncs.borrow_mut() += 1; + self.inner.sync(c) + } + + fn size(&self) -> Result { + self.inner.size() + } +} + +impl Drop for SimulatorFile { + fn drop(&mut self) { + self.inner.unlock_file().expect("Failed to unlock file"); + } +} diff --git a/simulator/simulator/io.rs b/simulator/simulator/io.rs new file mode 100644 index 000000000..e6b96218e --- /dev/null +++ b/simulator/simulator/io.rs @@ -0,0 +1,90 @@ +use std::{cell::RefCell, rc::Rc}; + +use limbo_core::{OpenFlags, PlatformIO, Result, IO}; +use rand::{RngCore, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +use crate::simulator::file::SimulatorFile; + +pub(crate) struct SimulatorIO { + pub(crate) inner: Box, + pub(crate) fault: RefCell, + pub(crate) files: RefCell>>, + pub(crate) rng: RefCell, + pub(crate) nr_run_once_faults: RefCell, + pub(crate) page_size: usize, +} + +impl SimulatorIO { + pub(crate) fn new(seed: u64, page_size: usize) -> Result { + let inner = Box::new(PlatformIO::new()?); + let fault = RefCell::new(false); + let files = RefCell::new(Vec::new()); + let rng = RefCell::new(ChaCha8Rng::seed_from_u64(seed)); + let nr_run_once_faults = RefCell::new(0); + Ok(Self { + inner, + fault, + files, + rng, + nr_run_once_faults, + page_size, + }) + } + + pub(crate) fn inject_fault(&self, fault: bool) { + self.fault.replace(fault); + for file in self.files.borrow().iter() { + file.inject_fault(fault); + } + } + + pub(crate) fn print_stats(&self) { + println!("run_once faults: {}", self.nr_run_once_faults.borrow()); + for file in self.files.borrow().iter() { + file.print_stats(); + } + } +} + +impl IO for SimulatorIO { + fn open_file( + &self, + path: &str, + flags: OpenFlags, + _direct: bool, + ) -> Result> { + let inner = self.inner.open_file(path, flags, false)?; + let file = Rc::new(SimulatorFile { + inner, + fault: RefCell::new(false), + nr_pread_faults: RefCell::new(0), + nr_pwrite_faults: RefCell::new(0), + reads: RefCell::new(0), + writes: RefCell::new(0), + syncs: RefCell::new(0), + page_size: self.page_size, + }); + self.files.borrow_mut().push(file.clone()); + Ok(file) + } + + fn run_once(&self) -> Result<()> { + if *self.fault.borrow() { + *self.nr_run_once_faults.borrow_mut() += 1; + return Err(limbo_core::LimboError::InternalError( + "Injected fault".into(), + )); + } + self.inner.run_once().unwrap(); + Ok(()) + } + + fn generate_random_number(&self) -> i64 { + self.rng.borrow_mut().next_u64() as i64 + } + + fn get_current_time(&self) -> String { + "2024-01-01 00:00:00".to_string() + } +} diff --git a/simulator/simulator/mod.rs b/simulator/simulator/mod.rs new file mode 100644 index 000000000..d5f06c103 --- /dev/null +++ b/simulator/simulator/mod.rs @@ -0,0 +1,4 @@ +pub mod cli; +pub mod env; +pub mod file; +pub mod io; From 4f07342fdcc107728f4d87c072edd370aba9c29e Mon Sep 17 00:00:00 2001 From: alpaylan Date: Sun, 22 Dec 2024 23:25:35 -0500 Subject: [PATCH 3/4] catch panics, add doublecheck --- simulator/main.rs | 116 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 12 deletions(-) diff --git a/simulator/main.rs b/simulator/main.rs index 6a09e4f4a..51a75de96 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -9,6 +9,7 @@ use rand_chacha::ChaCha8Rng; use simulator::cli::SimulatorCLI; use simulator::env::{SimConnection, SimulatorEnv, SimulatorOpts}; use simulator::io::SimulatorIO; +use std::backtrace::Backtrace; use std::io::Write; use std::path::Path; use std::rc::Rc; @@ -24,15 +25,15 @@ mod simulator; fn main() { let _ = env_logger::try_init(); - let opts = SimulatorCLI::parse(); + let cli_opts = SimulatorCLI::parse(); - let seed = match opts.seed { + let seed = match cli_opts.seed { Some(seed) => seed, None => rand::thread_rng().next_u64(), }; - let output_dir = match opts.output_dir { - Some(dir) => Path::new(&dir).to_path_buf(), + let output_dir = match &cli_opts.output_dir { + Some(dir) => Path::new(dir).to_path_buf(), None => TempDir::new().unwrap().into_path(), }; @@ -44,6 +45,98 @@ fn main() { log::info!("simulator plan path: {:?}", plan_path); log::info!("seed: {}", seed); + std::panic::set_hook(Box::new(move |info| { + log::error!("panic occurred"); + + let payload = info.payload(); + if let Some(s) = payload.downcast_ref::<&str>() { + log::error!("{}", s); + } else if let Some(s) = payload.downcast_ref::() { + log::error!("{}", s); + } else { + log::error!("unknown panic payload"); + } + + let bt = Backtrace::force_capture(); + log::error!("captured backtrace:\n{}", bt); + })); + + let result = std::panic::catch_unwind(|| run_simulation(seed, &cli_opts, &db_path, &plan_path)); + + if cli_opts.doublecheck { + // Move the old database and plan file to a new location + let old_db_path = db_path.with_extension("_old.db"); + let old_plan_path = plan_path.with_extension("_old.plan"); + + std::fs::rename(&db_path, &old_db_path).unwrap(); + std::fs::rename(&plan_path, &old_plan_path).unwrap(); + + // Run the simulation again + let result2 = + std::panic::catch_unwind(|| run_simulation(seed, &cli_opts, &db_path, &plan_path)); + + match (result, result2) { + (Ok(Ok(_)), Err(_)) => { + log::error!("doublecheck failed! first run succeeded, but second run panicked."); + } + (Ok(Err(_)), Err(_)) => { + log::error!( + "doublecheck failed! first run failed assertion, but second run panicked." + ); + } + (Err(_), Ok(Ok(_))) => { + log::error!("doublecheck failed! first run panicked, but second run succeeded."); + } + (Err(_), Ok(Err(_))) => { + log::error!( + "doublecheck failed! first run panicked, but second run failed assertion." + ); + } + (Ok(Ok(_)), Ok(Err(_))) => { + log::error!( + "doublecheck failed! first run succeeded, but second run failed assertion." + ); + } + (Ok(Err(_)), Ok(Ok(_))) => { + log::error!( + "doublecheck failed! first run failed assertion, but second run succeeded." + ); + } + (Err(_), Err(_)) | (Ok(_), Ok(_)) => { + // Compare the two database files byte by byte + let old_db = std::fs::read(&old_db_path).unwrap(); + let new_db = std::fs::read(&db_path).unwrap(); + if old_db != new_db { + log::error!("doublecheck failed! database files are different."); + } else { + log::info!("doublecheck succeeded! database files are the same."); + } + } + } + + // Move the new database and plan file to a new location + let new_db_path = db_path.with_extension("_double.db"); + let new_plan_path = plan_path.with_extension("_double.plan"); + + std::fs::rename(&db_path, &new_db_path).unwrap(); + std::fs::rename(&plan_path, &new_plan_path).unwrap(); + + // Move the old database and plan file back + std::fs::rename(&old_db_path, &db_path).unwrap(); + std::fs::rename(&old_plan_path, &plan_path).unwrap(); + } + // Print the seed, the locations of the database and the plan file at the end again for easily accessing them. + log::info!("database path: {:?}", db_path); + log::info!("simulator plan path: {:?}", plan_path); + log::info!("seed: {}", seed); +} + +fn run_simulation( + seed: u64, + cli_opts: &SimulatorCLI, + db_path: &Path, + plan_path: &Path, +) -> Result<()> { let mut rng = ChaCha8Rng::seed_from_u64(seed); let (read_percent, write_percent, delete_percent) = { @@ -57,7 +150,7 @@ fn main() { }; let opts = SimulatorOpts { - ticks: rng.gen_range(0..opts.maximum_size), + ticks: rng.gen_range(0..cli_opts.maximum_size), max_connections: 1, // TODO: for now let's use one connection as we didn't implement // correct transactions procesing max_tables: rng.gen_range(0..128), @@ -65,7 +158,7 @@ fn main() { write_percent, delete_percent, page_size: 4096, // TODO: randomize this too - max_interactions: rng.gen_range(0..opts.maximum_size), + max_interactions: rng.gen_range(0..cli_opts.maximum_size), }; let io = Arc::new(SimulatorIO::new(seed, opts.page_size).unwrap()); @@ -92,7 +185,7 @@ fn main() { .map(|_| InteractionPlan::arbitrary_from(&mut env.rng.clone(), &env)) .collect::>(); - let mut f = std::fs::File::create(plan_path.clone()).unwrap(); + let mut f = std::fs::File::create(plan_path).unwrap(); // todo: create a detailed plan file with all the plans. for now, we only use 1 connection, so it's safe to use the first plan. f.write(plans[0].to_string().as_bytes()).unwrap(); @@ -102,15 +195,14 @@ fn main() { let result = execute_plans(&mut env, &mut plans); if result.is_err() { - log::error!("error executing plans: {:?}", result.err()); + log::error!("error executing plans: {:?}", result.as_ref().err()); } env.io.print_stats(); - // Print the seed, the locations of the database and the plan file at the end again for easily accessing them. - log::info!("database path: {:?}", db_path); - log::info!("simulator plan path: {:?}", plan_path); - log::info!("seed: {}", seed); + log::info!("Simulation completed"); + + result } fn execute_plans(env: &mut SimulatorEnv, plans: &mut Vec) -> Result<()> { From 2186b3973b84ca05494db3ad1e6680f106c09709 Mon Sep 17 00:00:00 2001 From: alpaylan Date: Mon, 23 Dec 2024 16:16:39 -0500 Subject: [PATCH 4/4] change the name of the simulator submodule into runner --- simulator/main.rs | 8 ++++---- simulator/{simulator => runner}/cli.rs | 0 simulator/{simulator => runner}/env.rs | 2 +- simulator/{simulator => runner}/file.rs | 0 simulator/{simulator => runner}/io.rs | 2 +- simulator/{simulator => runner}/mod.rs | 0 6 files changed, 6 insertions(+), 6 deletions(-) rename simulator/{simulator => runner}/cli.rs (100%) rename simulator/{simulator => runner}/env.rs (96%) rename simulator/{simulator => runner}/file.rs (100%) rename simulator/{simulator => runner}/io.rs (98%) rename simulator/{simulator => runner}/mod.rs (100%) diff --git a/simulator/main.rs b/simulator/main.rs index 51a75de96..2b5eee3f6 100644 --- a/simulator/main.rs +++ b/simulator/main.rs @@ -6,9 +6,9 @@ use model::query::{Create, Query}; use model::table::{Column, Name, Table, Value}; use rand::prelude::*; use rand_chacha::ChaCha8Rng; -use simulator::cli::SimulatorCLI; -use simulator::env::{SimConnection, SimulatorEnv, SimulatorOpts}; -use simulator::io::SimulatorIO; +use runner::cli::SimulatorCLI; +use runner::env::{SimConnection, SimulatorEnv, SimulatorOpts}; +use runner::io::SimulatorIO; use std::backtrace::Backtrace; use std::io::Write; use std::path::Path; @@ -19,7 +19,7 @@ use tempfile::TempDir; mod generation; mod model; mod properties; -mod simulator; +mod runner; #[allow(clippy::arc_with_non_send_sync)] fn main() { diff --git a/simulator/simulator/cli.rs b/simulator/runner/cli.rs similarity index 100% rename from simulator/simulator/cli.rs rename to simulator/runner/cli.rs diff --git a/simulator/simulator/env.rs b/simulator/runner/env.rs similarity index 96% rename from simulator/simulator/env.rs rename to simulator/runner/env.rs index 0107ac501..0624b94b4 100644 --- a/simulator/simulator/env.rs +++ b/simulator/runner/env.rs @@ -6,7 +6,7 @@ use rand_chacha::ChaCha8Rng; use crate::model::table::Table; -use crate::simulator::io::SimulatorIO; +use crate::runner::io::SimulatorIO; pub(crate) struct SimulatorEnv { pub(crate) opts: SimulatorOpts, diff --git a/simulator/simulator/file.rs b/simulator/runner/file.rs similarity index 100% rename from simulator/simulator/file.rs rename to simulator/runner/file.rs diff --git a/simulator/simulator/io.rs b/simulator/runner/io.rs similarity index 98% rename from simulator/simulator/io.rs rename to simulator/runner/io.rs index e6b96218e..c039764b0 100644 --- a/simulator/simulator/io.rs +++ b/simulator/runner/io.rs @@ -4,7 +4,7 @@ use limbo_core::{OpenFlags, PlatformIO, Result, IO}; use rand::{RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; -use crate::simulator::file::SimulatorFile; +use crate::runner::file::SimulatorFile; pub(crate) struct SimulatorIO { pub(crate) inner: Box, diff --git a/simulator/simulator/mod.rs b/simulator/runner/mod.rs similarity index 100% rename from simulator/simulator/mod.rs rename to simulator/runner/mod.rs