pub use crate::moira::{Task, WriteType};
pub use crate::types::{
Block, Command, ExecState, MergeCommand, MoiraCommand, Transaction, TransactionResult,
};
use futures::stream::futures_unordered::FuturesUnordered;
use futures::StreamExt;
use kvstore::KVAdapter;
use multistore::MultiVersionedStore;
use serde::{de::DeserializeOwned, Serialize};
use std::collections::VecDeque;
use std::fmt::Debug;
use std::hash::Hash;
use std::sync::Arc;
use tokio::sync::mpsc;
pub mod kvstore;
pub mod moira;
pub mod multistore;
pub mod types;
pub async fn execute_block<K, V, C>(
transactions: Block<K, C>,
kv_adapter: KVAdapter<K, V>,
cores: usize,
) where
K: 'static + Send + Sync + Serialize + Eq + Hash + Clone + Debug,
V: 'static + Send + Sync + Serialize + DeserializeOwned + Debug,
C: 'static + Send + Sync + Debug + Command<K, V>,
{
const CONCURRENT_FUTURES: usize = 200;
let mut multi_versioned_store = MultiVersionedStore::<K, V, C>::new(kv_adapter);
multi_versioned_store.prepare(&transactions);
let (command_sender, mut command_receiver) = mpsc::unbounded_channel::<MoiraCommand<K, V, C>>();
let mut inner_blocks = Vec::<VecDeque<Arc<Transaction<K, C>>>>::with_capacity(cores);
for _ in 0..cores {
inner_blocks.push(VecDeque::with_capacity(1 + transactions.len() / cores));
}
for i in 0..transactions.len() {
inner_blocks[i % cores].push_back(transactions[i].clone());
}
while let Some(mut inner_block) = inner_blocks.pop() {
let inner_command_sender = command_sender.clone();
tokio::spawn(async move {
let mut transaction_bag = FuturesUnordered::new();
let mut idx = 0;
while let Some(transaction) = inner_block.pop_front() {
let moira_task = Task::new(transaction.clone(), inner_command_sender.clone());
let fut = moira_task.run_command(); transaction_bag.push(fut);
idx += 1;
if idx == CONCURRENT_FUTURES {
break;
}
}
while let Some(_) = transaction_bag.next().await {
if let Some(transaction) = inner_block.pop_front() {
let mdb = moira::Task::new(transaction.clone(), inner_command_sender.clone());
let fut = mdb.run_command(); transaction_bag.push(fut);
}
}
drop(inner_command_sender);
});
}
drop(command_sender);
let mut reschedule = VecDeque::new();
while let Some(cmd) = command_receiver.recv().await {
match cmd {
MoiraCommand::Read(key, seq, response) => {
multi_versioned_store.read(&key, seq, response).await;
}
MoiraCommand::Outcome(db) => {
let final_outcome = db.outcome;
match final_outcome {
TransactionResult::Commit => {
tracing::debug!("Commit {}", db.seq);
for key in &db.write_set {
if db.kv_store.contains_key(&key) {
match &db.kv_store[&key] {
WriteType::Write(val) => {
multi_versioned_store
.write(
&key,
Some(val.clone()),
db.seq,
ExecState::Commit,
)
.await;
}
WriteType::Merge(val) => {
tracing::debug!("Merge to key {:?}", key);
multi_versioned_store
.write(
&key,
Some(val.clone()),
db.seq,
ExecState::Merge,
)
.await;
}
};
} else {
multi_versioned_store
.write(&key, None, db.seq, ExecState::NoWrite)
.await;
}
}
}
TransactionResult::Abort(_) => {
tracing::debug!("Abort {}", db.seq);
for key in &db.write_set {
multi_versioned_store
.write(key, None, db.seq, ExecState::Abort)
.await;
}
}
TransactionResult::Reschedule => {
tracing::debug!("Reschedule {}", db.seq);
for key in &db.write_set {
multi_versioned_store
.write(key, None, db.seq, ExecState::Reschedule)
.await;
}
reschedule.push_back(db.transaction.clone());
}
}
}
}
}
multi_versioned_store.commit_all_changes().await;
}