use crate::{
executor::ParallelTransactionExecutor,
proptest_types::types::{ExpectedOutput, Task, Transaction},
scheduler::{Scheduler, SchedulerTask, TaskGuard},
};
use rand::random;
use std::{
fmt::Debug,
hash::Hash,
sync::{atomic::AtomicUsize, Arc},
};
fn run_and_assert<K, V>(transactions: Vec<Transaction<K, V>>)
where
K: PartialOrd + Send + Sync + Clone + Hash + Eq + 'static,
V: Send + Sync + Debug + Clone + Eq + 'static,
{
let output = ParallelTransactionExecutor::<Transaction<K, V>, Task<K, V>>::new(num_cpus::get())
.execute_transactions_parallel((), transactions.clone());
let baseline = ExpectedOutput::generate_baseline(&transactions);
assert!(baseline.check_output(&output))
}
const TOTAL_KEY_NUM: u64 = 50;
const WRITES_PER_KEY: u64 = 100;
#[test]
fn cycle_transactions() {
let mut transactions = vec![];
for _ in 0..TOTAL_KEY_NUM {
let key = random::<[u8; 32]>();
for _ in 0..WRITES_PER_KEY {
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![vec![key]],
writes: vec![vec![(key, random::<u64>())]],
})
}
}
run_and_assert(transactions)
}
const NUM_BLOCKS: u64 = 10;
const TXN_PER_BLOCK: u64 = 100;
#[test]
fn one_reads_all_barrier() {
let mut transactions = vec![];
let keys: Vec<_> = (0..TXN_PER_BLOCK).map(|_| random::<[u8; 32]>()).collect();
for _ in 0..NUM_BLOCKS {
for key in &keys {
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![vec![*key]],
writes: vec![vec![(*key, random::<u64>())]],
})
}
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![keys.clone()],
writes: vec![vec![]],
})
}
run_and_assert(transactions)
}
#[test]
fn one_writes_all_barrier() {
let mut transactions = vec![];
let keys: Vec<_> = (0..TXN_PER_BLOCK).map(|_| random::<[u8; 32]>()).collect();
for _ in 0..NUM_BLOCKS {
for key in &keys {
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![vec![*key]],
writes: vec![vec![(*key, random::<u64>())]],
})
}
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![keys.clone()],
writes: vec![keys
.iter()
.map(|key| (*key, random::<u64>()))
.collect::<Vec<_>>()],
})
}
run_and_assert(transactions)
}
#[test]
fn early_aborts() {
let mut transactions = vec![];
let keys: Vec<_> = (0..TXN_PER_BLOCK).map(|_| random::<[u8; 32]>()).collect();
for _ in 0..NUM_BLOCKS {
for key in &keys {
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![vec![*key]],
writes: vec![vec![(*key, random::<u64>())]],
})
}
transactions.push(Transaction::Abort)
}
run_and_assert(transactions)
}
#[test]
fn early_skips() {
let mut transactions = vec![];
let keys: Vec<_> = (0..TXN_PER_BLOCK).map(|_| random::<[u8; 32]>()).collect();
for _ in 0..NUM_BLOCKS {
for key in &keys {
transactions.push(Transaction::Write {
incarnation: Arc::new(AtomicUsize::new(0)),
reads: vec![vec![*key]],
writes: vec![vec![(*key, random::<u64>())]],
})
}
transactions.push(Transaction::SkipRest)
}
run_and_assert(transactions)
}
#[test]
fn scheduler_tasks() {
let s = Scheduler::new(6);
let fake_counter = AtomicUsize::new(0);
for i in 0..5 {
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((j, 0), None, _) if i == j
));
}
assert!(matches!(
s.finish_execution(0, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((0, 0), _)
));
assert!(matches!(
s.finish_execution(2, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.finish_execution(4, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((2, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((4, 0), _)
));
assert!(matches!(
s.finish_execution(3, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((3, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((4, 0), _)
));
assert!(s.try_abort(3, 0));
assert!(matches!(
s.finish_execution(1, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((1, 0), _)
));
assert!(!s.try_abort(3, 0));
assert!(matches!(
s.finish_abort(3, 0, TaskGuard::new(&fake_counter)),
SchedulerTask::ExecutionTask((3, 1), None, _)
));
assert!(s.try_abort(4, 0));
assert!(matches!(
s.finish_abort(4, 0, TaskGuard::new(&fake_counter)),
SchedulerTask::ExecutionTask((4, 1), None, _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((5, 0), None, _)
));
assert!(matches!(
s.finish_execution(4, 1, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((4, 1), _)
));
assert!(matches!(
s.finish_execution(3, 1, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((3, 1), _)
));
assert!(matches!(
s.finish_execution(5, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((5, 0), _)
));
assert!(matches!(s.next_task(), SchedulerTask::Done));
}
#[test]
fn scheduler_dependency() {
let s = Scheduler::new(10);
let fake_counter = AtomicUsize::new(0);
for i in 0..5 {
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((j, 0), None, _) if j == i
));
}
assert!(matches!(
s.finish_execution(0, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((0, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((5, 0), None, _)
));
assert!(s.wait_for_dependency(3, 0).is_none());
assert!(s.wait_for_dependency(4, 2).is_some());
assert!(matches!(
s.finish_execution(2, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((2, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((4, 0), Some(_), _)
));
}
#[test]
fn scheduler_incarnation() {
let s = Scheduler::new(5);
let fake_counter = AtomicUsize::new(0);
for i in 0..5 {
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((j, 0), None, _) if j == i
));
}
assert!(s.wait_for_dependency(1, 0).is_some());
assert!(s.wait_for_dependency(3, 0).is_some());
assert!(matches!(
s.finish_execution(2, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.finish_execution(4, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((2, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((4, 0), _)
));
assert!(s.try_abort(2, 0));
assert!(s.try_abort(4, 0));
assert!(!s.try_abort(2, 0));
assert!(matches!(
s.finish_abort(2, 0, TaskGuard::new(&fake_counter)),
SchedulerTask::ExecutionTask((2, 1), None, _)
));
assert!(matches!(
s.finish_execution(0, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((0, 0), _)
));
assert!(matches!(
s.finish_abort(4, 0, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((1, 0), Some(_), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((3, 0), Some(_), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((4, 1), None, _)
));
assert!(matches!(
s.finish_execution(1, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((1, 0), _)
));
assert!(matches!(
s.finish_execution(2, 1, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((2, 1), _)
));
assert!(matches!(
s.finish_execution(3, 0, false, TaskGuard::new(&fake_counter)),
SchedulerTask::ValidationTask((3, 0), _)
));
assert!(matches!(
s.finish_execution(4, 1, false, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((4, 1), _)
));
assert!(matches!(s.next_task(), SchedulerTask::Done));
}
#[test]
fn scheduler_stop_idx() {
let s = Scheduler::new(3);
let fake_counter = AtomicUsize::new(0);
for i in 0..2 {
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((j, 0), None, _) if j == i
));
}
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((2, 0), None, _)
));
assert!(matches!(
s.finish_execution(0, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.finish_execution(1, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((0, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((1, 0), _)
));
assert!(matches!(
s.finish_execution(2, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((2, 0), _)
));
assert!(matches!(s.next_task(), SchedulerTask::Done));
}
#[test]
fn scheduler_drain_idx() {
let s = Scheduler::new(3);
let fake_counter = AtomicUsize::new(0);
for i in 0..3 {
assert!(matches!(
s.next_task(),
SchedulerTask::ExecutionTask((j, 0), None, _) if j == i
));
}
assert!(matches!(
s.finish_execution(0, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.finish_execution(1, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((0, 0), _)
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((1, 0), _)
));
assert!(matches!(
s.finish_execution(2, 0, true, TaskGuard::new(&fake_counter)),
SchedulerTask::NoTask
));
assert!(matches!(
s.next_task(),
SchedulerTask::ValidationTask((2, 0), _)
));
assert!(matches!(s.next_task(), SchedulerTask::Done));
}