From c4843d6a6ede9f66f6ba560e87fb917ca2c54d4c Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 18 Sep 2025 16:37:22 -0300 Subject: [PATCH] refactor SimulatorEnv to hold committed tables and separate connection tables for snapshot isolation --- simulator/model/mod.rs | 2 ++ simulator/runner/differential.rs | 4 +-- simulator/runner/doublecheck.rs | 4 +-- simulator/runner/env.rs | 60 ++++++++++++++++++++++++++++---- simulator/runner/execution.rs | 2 +- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/simulator/model/mod.rs b/simulator/model/mod.rs index 20adbbe9d..237750683 100644 --- a/simulator/model/mod.rs +++ b/simulator/model/mod.rs @@ -358,6 +358,8 @@ impl Shadow for Select { impl Shadow for Begin { type Result = Vec>; fn shadow(&self, tables: &mut SimulatorTables) -> Self::Result { + // FIXME: currently the snapshot is taken eagerly + // this is wrong for Deffered transactions tables.snapshot = Some(tables.tables.clone()); vec![] } diff --git a/simulator/runner/differential.rs b/simulator/runner/differential.rs index 6dd5803ee..c2be34b38 100644 --- a/simulator/runner/differential.rs +++ b/simulator/runner/differential.rs @@ -59,8 +59,8 @@ pub(crate) fn execute_interactions( let mut env = env.lock().unwrap(); let mut rusqlite_env = rusqlite_env.lock().unwrap(); - env.tables.clear(); - rusqlite_env.tables.clear(); + env.clear_tables(); + rusqlite_env.clear_tables(); let now = std::time::Instant::now(); diff --git a/simulator/runner/doublecheck.rs b/simulator/runner/doublecheck.rs index a2c98b424..d90408686 100644 --- a/simulator/runner/doublecheck.rs +++ b/simulator/runner/doublecheck.rs @@ -89,8 +89,8 @@ pub(crate) fn execute_plans( let mut env = env.lock().unwrap(); let mut doublecheck_env = doublecheck_env.lock().unwrap(); - env.tables.clear(); - doublecheck_env.tables.clear(); + env.clear_tables(); + doublecheck_env.clear_tables(); let now = std::time::Instant::now(); diff --git a/simulator/runner/env.rs b/simulator/runner/env.rs index c5423f97c..37c17966a 100644 --- a/simulator/runner/env.rs +++ b/simulator/runner/env.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use garde::Validate; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; +use sql_generation::generation::GenerationContext; use sql_generation::model::table::Table; use turso_core::Database; @@ -71,8 +72,12 @@ pub(crate) struct SimulatorEnv { pub(crate) paths: Paths, pub(crate) type_: SimulationType, pub(crate) phase: SimulationPhase, - pub(crate) tables: SimulatorTables, pub memory_io: bool, + + /// If connection state is None, means we are not in a transaction + pub connection_tables: Vec>, + // Table data that is committed into the database or wal + pub committed_tables: SimulatorTables, } impl UnwindSafe for SimulatorEnv {} @@ -81,10 +86,6 @@ impl SimulatorEnv { pub(crate) fn clone_without_connections(&self) -> Self { SimulatorEnv { opts: self.opts.clone(), - tables: self.tables.clone(), - connections: (0..self.connections.len()) - .map(|_| SimConnection::Disconnected) - .collect(), io: self.io.clone(), db: self.db.clone(), rng: self.rng.clone(), @@ -93,11 +94,17 @@ impl SimulatorEnv { phase: self.phase, memory_io: self.memory_io, profile: self.profile.clone(), + connections: (0..self.connections.len()) + .map(|_| SimConnection::Disconnected) + .collect(), + // TODO: not sure if connection_tables should be recreated instead + connection_tables: self.connection_tables.clone(), + committed_tables: self.committed_tables.clone(), } } pub(crate) fn clear(&mut self) { - self.tables.clear(); + self.clear_tables(); self.connections.iter_mut().for_each(|c| c.disconnect()); self.rng = ChaCha8Rng::seed_from_u64(self.opts.seed); @@ -284,7 +291,6 @@ impl SimulatorEnv { SimulatorEnv { opts, - tables: SimulatorTables::new(), connections, paths, rng, @@ -294,6 +300,8 @@ impl SimulatorEnv { phase: SimulationPhase::Test, memory_io: cli_opts.memory_io, profile: profile.clone(), + committed_tables: SimulatorTables::new(), + connection_tables: vec![None; profile.max_connections], } } @@ -327,6 +335,44 @@ impl SimulatorEnv { } }; } + + /// Clears the commited tables and the connection tables + pub fn clear_tables(&mut self) { + self.committed_tables.clear(); + self.connection_tables.iter_mut().for_each(|t| { + if let Some(t) = t { + t.clear(); + } + }); + } + + pub fn connection_context(&self, connection_index: usize) -> impl GenerationContext { + struct ConnectionGenContext<'a> { + tables: &'a Vec, + opts: &'a sql_generation::generation::Opts, + } + + impl<'a> GenerationContext for ConnectionGenContext<'a> { + fn tables(&self) -> &Vec { + self.tables + } + + fn opts(&self) -> &sql_generation::generation::Opts { + self.opts + } + } + + let tables = if let Some(tables) = self.connection_tables.get(connection_index).unwrap() { + &tables.tables + } else { + &self.committed_tables.tables + }; + + ConnectionGenContext { + opts: &self.profile.query.gen_opts, + tables, + } + } } pub trait ConnectionTrait diff --git a/simulator/runner/execution.rs b/simulator/runner/execution.rs index 5c3f81a6b..2ff819f22 100644 --- a/simulator/runner/execution.rs +++ b/simulator/runner/execution.rs @@ -65,7 +65,7 @@ pub(crate) fn execute_interactions( env.clear_poison(); let mut env = env.lock().unwrap(); - env.tables.clear(); + env.clear_tables(); for _tick in 0..env.opts.ticks { tracing::trace!("Executing tick {}", _tick);