mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-25 12:04:21 +01:00
250 lines
9.7 KiB
Rust
250 lines
9.7 KiB
Rust
use std::sync::{Arc, Mutex};
|
|
|
|
use crate::generation::plan::{ConnectionState, Interaction, InteractionPlanState};
|
|
|
|
use super::{
|
|
env::SimulatorEnv,
|
|
execution::{Execution, ExecutionHistory, ExecutionResult},
|
|
};
|
|
|
|
pub fn run_simulation(
|
|
env: Arc<Mutex<SimulatorEnv>>,
|
|
rusqlite_env: Arc<Mutex<SimulatorEnv>>,
|
|
plan: Vec<Interaction>,
|
|
last_execution: Arc<Mutex<Execution>>,
|
|
) -> ExecutionResult {
|
|
tracing::info!("Executing database interaction plan...");
|
|
|
|
let num_conns = {
|
|
let env = env.lock().unwrap();
|
|
env.connections.len()
|
|
};
|
|
|
|
let mut conn_states = (0..num_conns)
|
|
.map(|_| ConnectionState::default())
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut rusqlite_states = conn_states.clone();
|
|
|
|
let mut state = InteractionPlanState {
|
|
interaction_pointer: 0,
|
|
};
|
|
|
|
let result = execute_interactions(
|
|
env,
|
|
rusqlite_env,
|
|
plan,
|
|
&mut state,
|
|
&mut conn_states,
|
|
&mut rusqlite_states,
|
|
last_execution,
|
|
);
|
|
|
|
tracing::info!("Simulation completed");
|
|
|
|
result
|
|
}
|
|
|
|
pub(crate) fn execute_interactions(
|
|
env: Arc<Mutex<SimulatorEnv>>,
|
|
rusqlite_env: Arc<Mutex<SimulatorEnv>>,
|
|
interactions: Vec<Interaction>,
|
|
state: &mut InteractionPlanState,
|
|
conn_states: &mut [ConnectionState],
|
|
rusqlite_states: &mut [ConnectionState],
|
|
last_execution: Arc<Mutex<Execution>>,
|
|
) -> ExecutionResult {
|
|
let mut history = ExecutionHistory::new();
|
|
|
|
let mut env = env.lock().unwrap();
|
|
let mut rusqlite_env = rusqlite_env.lock().unwrap();
|
|
|
|
env.tables.clear();
|
|
rusqlite_env.tables.clear();
|
|
|
|
let now = std::time::Instant::now();
|
|
|
|
for _tick in 0..env.opts.ticks {
|
|
if state.interaction_pointer >= interactions.len() {
|
|
break;
|
|
}
|
|
|
|
let interaction = &interactions[state.interaction_pointer];
|
|
|
|
let connection_index = interaction.connection_index;
|
|
let turso_conn_state = &mut conn_states[connection_index];
|
|
let rusqlite_conn_state = &mut rusqlite_states[connection_index];
|
|
|
|
history
|
|
.history
|
|
.push(Execution::new(connection_index, state.interaction_pointer));
|
|
let mut last_execution = last_execution.lock().unwrap();
|
|
last_execution.connection_index = connection_index;
|
|
last_execution.interaction_index = state.interaction_pointer;
|
|
|
|
let mut turso_state = state.clone();
|
|
|
|
// first execute turso
|
|
let turso_res = super::execution::execute_plan(
|
|
&mut env,
|
|
interaction,
|
|
turso_conn_state,
|
|
&mut turso_state,
|
|
);
|
|
|
|
let mut rusqlite_state = state.clone();
|
|
|
|
// second execute rusqlite
|
|
let rusqlite_res = super::execution::execute_plan(
|
|
&mut rusqlite_env,
|
|
interaction,
|
|
rusqlite_conn_state,
|
|
&mut rusqlite_state,
|
|
);
|
|
|
|
// Compare results
|
|
if let Err(err) = compare_results(
|
|
turso_res,
|
|
turso_conn_state,
|
|
rusqlite_res,
|
|
rusqlite_conn_state,
|
|
) {
|
|
return ExecutionResult::new(history, Some(err));
|
|
}
|
|
|
|
state.interaction_pointer += 1;
|
|
|
|
// Check if the maximum time for the simulation has been reached
|
|
if now.elapsed().as_secs() >= env.opts.max_time_simulation as u64 {
|
|
return ExecutionResult::new(
|
|
history,
|
|
Some(turso_core::LimboError::InternalError(
|
|
"maximum time for simulation reached".into(),
|
|
)),
|
|
);
|
|
}
|
|
}
|
|
|
|
ExecutionResult::new(history, None)
|
|
}
|
|
|
|
fn compare_results(
|
|
turso_res: turso_core::Result<()>,
|
|
turso_conn_state: &mut ConnectionState,
|
|
rusqlite_res: turso_core::Result<()>,
|
|
rusqlite_conn_state: &mut ConnectionState,
|
|
) -> turso_core::Result<()> {
|
|
match (turso_res, rusqlite_res) {
|
|
(Ok(..), Ok(..)) => {
|
|
let limbo_values = turso_conn_state.stack.last();
|
|
let rusqlite_values = rusqlite_conn_state.stack.last();
|
|
match (limbo_values, rusqlite_values) {
|
|
(Some(limbo_values), Some(rusqlite_values)) => {
|
|
match (limbo_values, rusqlite_values) {
|
|
(Ok(limbo_values), Ok(rusqlite_values)) => {
|
|
if limbo_values != rusqlite_values {
|
|
tracing::error!(
|
|
"returned values from limbo and rusqlite results do not match"
|
|
);
|
|
let diff = limbo_values
|
|
.iter()
|
|
.zip(rusqlite_values.iter())
|
|
.enumerate()
|
|
.filter(|(_, (l, r))| l != r)
|
|
.collect::<Vec<_>>();
|
|
|
|
let diff = diff
|
|
.iter()
|
|
.flat_map(|(i, (l, r))| {
|
|
let mut diffs = vec![];
|
|
for (j, (l, r)) in l.iter().zip(r.iter()).enumerate() {
|
|
if l != r {
|
|
tracing::debug!(
|
|
"difference at index {}, {}: {} != {}",
|
|
i,
|
|
j,
|
|
l.to_string(),
|
|
r.to_string()
|
|
);
|
|
diffs.push(((i, j), (l.clone(), r.clone())));
|
|
}
|
|
}
|
|
diffs
|
|
})
|
|
.collect::<Vec<_>>();
|
|
tracing::debug!("limbo values {:?}", limbo_values);
|
|
tracing::debug!("rusqlite values {:?}", rusqlite_values);
|
|
tracing::debug!(
|
|
"differences: {}",
|
|
diff.iter()
|
|
.map(|((i, j), (l, r))| format!(
|
|
"\t({i}, {j}): ({l}) != ({r})"
|
|
))
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
);
|
|
return Err(turso_core::LimboError::InternalError(
|
|
"returned values from limbo and rusqlite results do not match"
|
|
.into(),
|
|
));
|
|
}
|
|
}
|
|
(Err(limbo_err), Err(rusqlite_err)) => {
|
|
tracing::warn!("limbo and rusqlite both fail, requires manual check");
|
|
tracing::warn!("limbo error {}", limbo_err);
|
|
tracing::warn!("rusqlite error {}", rusqlite_err);
|
|
}
|
|
(Ok(limbo_result), Err(rusqlite_err)) => {
|
|
tracing::error!(
|
|
"limbo and rusqlite results do not match, limbo returned values but rusqlite failed"
|
|
);
|
|
tracing::error!("limbo values {:?}", limbo_result);
|
|
tracing::error!("rusqlite error {}", rusqlite_err);
|
|
return Err(turso_core::LimboError::InternalError(
|
|
"limbo and rusqlite results do not match".into(),
|
|
));
|
|
}
|
|
(Err(limbo_err), Ok(_)) => {
|
|
tracing::error!(
|
|
"limbo and rusqlite results do not match, limbo failed but rusqlite returned values"
|
|
);
|
|
tracing::error!("limbo error {}", limbo_err);
|
|
return Err(turso_core::LimboError::InternalError(
|
|
"limbo and rusqlite results do not match".into(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
(None, None) => {}
|
|
_ => {
|
|
tracing::error!("limbo and rusqlite results do not match");
|
|
return Err(turso_core::LimboError::InternalError(
|
|
"limbo and rusqlite results do not match".into(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
(Err(err), Ok(_)) => {
|
|
tracing::error!("limbo and rusqlite results do not match");
|
|
tracing::error!("limbo error {}", err);
|
|
return Err(err);
|
|
}
|
|
(Ok(val), Err(err)) => {
|
|
tracing::error!("limbo and rusqlite results do not match");
|
|
tracing::error!("limbo {:?}", val);
|
|
tracing::error!("rusqlite error {}", err);
|
|
return Err(err);
|
|
}
|
|
(Err(err), Err(err_rusqlite)) => {
|
|
tracing::error!("limbo and rusqlite both fail, requires manual check");
|
|
tracing::error!("limbo error {}", err);
|
|
tracing::error!("rusqlite error {}", err_rusqlite);
|
|
// todo: Previously, we returned an error here, but now we just log it.
|
|
// The problem is that the errors might be different, and we cannot
|
|
// just assume both of them being errors has the same semantics.
|
|
// return Err(err);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|