use diem_types::trusted_state::TrustedState;
use std::{
convert::Infallible,
sync::{Arc, RwLock},
};
pub trait StateStore {
type Error: std::error::Error + Send + Sync + 'static;
fn latest_state(&self) -> Result<Option<TrustedState>, Self::Error>;
fn latest_state_version(&self) -> Result<Option<u64>, Self::Error> {
Ok(self.latest_state()?.map(|s| s.version()))
}
fn store(&self, new_state: &TrustedState) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone)]
pub struct InMemoryStateStore(Arc<WriteThroughCache<NoopStateStore>>);
#[derive(Debug)]
struct NoopStateStore;
#[derive(Debug)]
pub struct WriteThroughCache<S> {
durable_state_cache: RwLock<Option<TrustedState>>,
state_store: S,
}
impl<S: StateStore> WriteThroughCache<S> {
pub fn new(state_store: S) -> Result<Self, S::Error> {
let latest_state = state_store.latest_state()?;
Ok(Self {
durable_state_cache: RwLock::new(latest_state),
state_store,
})
}
fn ratchet_cache(&self, new_state: &TrustedState) {
let mut durable_state_cache = self.durable_state_cache.write().unwrap();
let cache_version = durable_state_cache.as_ref().map(|s| s.version());
if Some(new_state.version()) > cache_version {
*durable_state_cache = Some(new_state.clone());
}
}
pub fn as_inner(&self) -> &S {
&self.state_store
}
}
impl<S: StateStore> StateStore for WriteThroughCache<S> {
type Error = S::Error;
fn latest_state(&self) -> Result<Option<TrustedState>, Self::Error> {
Ok(self.durable_state_cache.read().unwrap().clone())
}
fn latest_state_version(&self) -> Result<Option<u64>, Self::Error> {
Ok(self
.durable_state_cache
.read()
.unwrap()
.as_ref()
.map(|s| s.version()))
}
fn store(&self, new_state: &TrustedState) -> Result<(), Self::Error> {
if Some(new_state.version()) <= self.latest_state_version()? {
return Ok(());
}
self.state_store.store(new_state)?;
self.ratchet_cache(new_state);
Ok(())
}
}
impl InMemoryStateStore {
pub fn new() -> Self {
Self(Arc::new(WriteThroughCache::new(NoopStateStore).unwrap()))
}
}
impl StateStore for InMemoryStateStore {
type Error = Infallible;
fn latest_state(&self) -> Result<Option<TrustedState>, Self::Error> {
self.0.latest_state()
}
fn latest_state_version(&self) -> Result<Option<u64>, Self::Error> {
self.0.latest_state_version()
}
fn store(&self, new_state: &TrustedState) -> Result<(), Self::Error> {
self.0.store(new_state)
}
}
impl StateStore for NoopStateStore {
type Error = Infallible;
fn latest_state(&self) -> Result<Option<TrustedState>, Self::Error> {
Ok(None)
}
fn store(&self, _new_state: &TrustedState) -> Result<(), Self::Error> {
Ok(())
}
}