use std::any::type_name;
use std::collections::BTreeMap;
use std::error::Error as StdError;
use std::fmt;
use std::sync::{Arc, Mutex, MutexGuard};
use super::error::{Error, Mutation};
use super::manifest::{ManifestStore, ManifestUpdate, NamedRootUpdate, RootManifest};
use super::store::{BatchOp, Store};
use super::tree::Tree;
use super::Prolly;
#[cfg(feature = "async-store")]
use {
super::manifest::AsyncManifestStore,
super::store::{AsyncStore, SyncStoreAsAsync},
super::AsyncProlly,
std::future::Future,
std::pin::Pin,
};
#[derive(Clone, Debug, PartialEq)]
pub struct RootCondition {
pub name: Vec<u8>,
pub expected: Option<RootManifest>,
}
impl RootCondition {
pub fn new(name: Vec<u8>, expected: Option<RootManifest>) -> Self {
Self { name, expected }
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum RootWrite {
Put {
name: Vec<u8>,
manifest: RootManifest,
},
Delete {
name: Vec<u8>,
},
}
impl RootWrite {
pub fn name(&self) -> &[u8] {
match self {
Self::Put { name, .. } | Self::Delete { name } => name,
}
}
pub fn replacement(&self) -> Option<&RootManifest> {
match self {
Self::Put { manifest, .. } => Some(manifest),
Self::Delete { .. } => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TransactionNodeWrite {
Upsert { key: Vec<u8>, value: Vec<u8> },
Delete { key: Vec<u8> },
}
#[derive(Clone, Debug, PartialEq)]
pub struct TransactionConflict {
pub name: Vec<u8>,
pub expected: Option<RootManifest>,
pub current: Option<RootManifest>,
}
impl TransactionConflict {
pub fn new(
name: Vec<u8>,
expected: Option<RootManifest>,
current: Option<RootManifest>,
) -> Self {
Self {
name,
expected,
current,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TransactionUpdate {
Applied {
nodes_written: usize,
roots_written: usize,
},
Conflict(TransactionConflict),
}
impl TransactionUpdate {
pub fn is_applied(&self) -> bool {
matches!(self, Self::Applied { .. })
}
pub fn is_conflict(&self) -> bool {
matches!(self, Self::Conflict(_))
}
pub fn conflict(&self) -> Option<&TransactionConflict> {
match self {
Self::Applied { .. } => None,
Self::Conflict(conflict) => Some(conflict),
}
}
}
pub trait TransactionalStore: Store + ManifestStore {
fn supports_transactions(&self) -> bool {
false
}
fn commit_transaction(
&self,
_node_writes: &[TransactionNodeWrite],
_root_conditions: &[RootCondition],
_root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
Err(Error::UnsupportedTransactions {
store: type_name::<Self>(),
})
}
}
impl<T> TransactionalStore for Arc<T>
where
T: TransactionalStore,
{
fn supports_transactions(&self) -> bool {
(**self).supports_transactions()
}
fn commit_transaction(
&self,
node_writes: &[TransactionNodeWrite],
root_conditions: &[RootCondition],
root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
(**self).commit_transaction(node_writes, root_conditions, root_writes)
}
}
#[cfg(feature = "async-store")]
#[allow(async_fn_in_trait)]
pub trait AsyncTransactionalStore: AsyncStore + AsyncManifestStore {
fn supports_transactions(&self) -> bool {
false
}
async fn commit_transaction(
&self,
_node_writes: &[TransactionNodeWrite],
_root_conditions: &[RootCondition],
_root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
Err(Error::UnsupportedTransactions {
store: type_name::<Self>(),
})
}
}
#[cfg(feature = "async-store")]
impl<T> AsyncTransactionalStore for Arc<T>
where
T: AsyncTransactionalStore,
{
fn supports_transactions(&self) -> bool {
(**self).supports_transactions()
}
async fn commit_transaction(
&self,
node_writes: &[TransactionNodeWrite],
root_conditions: &[RootCondition],
root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
(**self)
.commit_transaction(node_writes, root_conditions, root_writes)
.await
}
}
#[cfg(feature = "async-store")]
impl<S> AsyncTransactionalStore for SyncStoreAsAsync<S>
where
S: TransactionalStore,
{
fn supports_transactions(&self) -> bool {
self.inner().supports_transactions()
}
async fn commit_transaction(
&self,
node_writes: &[TransactionNodeWrite],
root_conditions: &[RootCondition],
root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
self.inner()
.commit_transaction(node_writes, root_conditions, root_writes)
}
}
#[cfg(feature = "tokio")]
impl<S> AsyncTransactionalStore for super::store::TokioBlockingStore<S>
where
S: TransactionalStore + 'static,
{
fn supports_transactions(&self) -> bool {
self.inner().supports_transactions()
}
async fn commit_transaction(
&self,
node_writes: &[TransactionNodeWrite],
root_conditions: &[RootCondition],
root_writes: &[RootWrite],
) -> Result<TransactionUpdate, Error> {
let store = self.shared();
let node_writes = node_writes.to_vec();
let root_conditions = root_conditions.to_vec();
let root_writes = root_writes.to_vec();
tokio::task::spawn_blocking(move || {
store.commit_transaction(&node_writes, &root_conditions, &root_writes)
})
.await
.map_err(|err| Error::Store(Box::new(err)))?
}
}
#[derive(Debug)]
pub struct TransactionOverlayError {
message: String,
source: Option<Box<dyn StdError + Send + Sync>>,
}
impl TransactionOverlayError {
fn poisoned(err: impl fmt::Display) -> Self {
Self {
message: format!("transaction overlay lock poisoned: {err}"),
source: None,
}
}
fn store(err: impl StdError + Send + Sync + 'static) -> Self {
Self {
message: format!("base store error: {err}"),
source: Some(Box::new(err)),
}
}
}
impl fmt::Display for TransactionOverlayError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "transaction overlay error: {}", self.message)
}
}
impl StdError for TransactionOverlayError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.source
.as_ref()
.map(|err| err.as_ref() as &(dyn StdError + 'static))
}
}
#[derive(Default)]
struct TransactionState {
node_writes: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
root_reads: BTreeMap<Vec<u8>, Option<RootManifest>>,
root_writes: BTreeMap<Vec<u8>, RootWrite>,
}
impl TransactionState {
fn node_writes(&self) -> Vec<TransactionNodeWrite> {
self.node_writes
.iter()
.map(|(key, value)| match value {
Some(value) => TransactionNodeWrite::Upsert {
key: key.clone(),
value: value.clone(),
},
None => TransactionNodeWrite::Delete { key: key.clone() },
})
.collect()
}
fn root_conditions(&self) -> Vec<RootCondition> {
self.root_reads
.iter()
.map(|(name, expected)| RootCondition::new(name.clone(), expected.clone()))
.collect()
}
fn root_writes(&self) -> Vec<RootWrite> {
self.root_writes.values().cloned().collect()
}
}
#[derive(Clone)]
pub struct TransactionOverlayStore<'a, S> {
base: &'a S,
state: Arc<Mutex<TransactionState>>,
}
impl<'a, S> TransactionOverlayStore<'a, S> {
fn new(base: &'a S, state: Arc<Mutex<TransactionState>>) -> Self {
Self { base, state }
}
fn lock(&self) -> Result<MutexGuard<'_, TransactionState>, TransactionOverlayError> {
self.state.lock().map_err(TransactionOverlayError::poisoned)
}
}
impl<S> Store for TransactionOverlayStore<'_, S>
where
S: Store,
{
type Error = TransactionOverlayError;
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
let staged = self.lock()?.node_writes.get(key).cloned();
match staged {
Some(value) => Ok(value),
None => self.base.get(key).map_err(TransactionOverlayError::store),
}
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Self::Error> {
self.lock()?
.node_writes
.insert(key.to_vec(), Some(value.to_vec()));
Ok(())
}
fn delete(&self, key: &[u8]) -> Result<(), Self::Error> {
self.lock()?.node_writes.insert(key.to_vec(), None);
Ok(())
}
fn batch(&self, ops: &[BatchOp]) -> Result<(), Self::Error> {
let mut state = self.lock()?;
for op in ops {
match op {
BatchOp::Upsert { key, value } => {
state
.node_writes
.insert((*key).to_vec(), Some((*value).to_vec()));
}
BatchOp::Delete { key } => {
state.node_writes.insert((*key).to_vec(), None);
}
}
}
Ok(())
}
}
impl<S> ManifestStore for TransactionOverlayStore<'_, S>
where
S: Store + ManifestStore,
{
type Error = TransactionOverlayError;
fn get_root(&self, name: &[u8]) -> Result<Option<RootManifest>, Self::Error> {
if let Some(write) = self.lock()?.root_writes.get(name).cloned() {
return Ok(write.replacement().cloned());
}
let current = self
.base
.get_root(name)
.map_err(TransactionOverlayError::store)?;
let mut state = self.lock()?;
state
.root_reads
.entry(name.to_vec())
.or_insert_with(|| current.clone());
Ok(current)
}
fn put_root(&self, name: &[u8], manifest: &RootManifest) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Put {
name: name.to_vec(),
manifest: manifest.clone(),
},
);
Ok(())
}
fn delete_root(&self, name: &[u8]) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Delete {
name: name.to_vec(),
},
);
Ok(())
}
fn compare_and_swap_root(
&self,
name: &[u8],
expected: Option<&RootManifest>,
new: Option<&RootManifest>,
) -> Result<ManifestUpdate, Self::Error> {
let current = self.get_root(name)?;
if current.as_ref() != expected {
return Ok(ManifestUpdate::Conflict { current });
}
match new {
Some(manifest) => self.put_root(name, manifest)?,
None => self.delete_root(name)?,
}
Ok(ManifestUpdate::Applied)
}
}
#[derive(Clone)]
pub struct OwnedTransactionOverlayStore<S> {
base: S,
state: Arc<Mutex<TransactionState>>,
}
impl<S> OwnedTransactionOverlayStore<S> {
fn new(base: S, state: Arc<Mutex<TransactionState>>) -> Self {
Self { base, state }
}
fn lock(&self) -> Result<MutexGuard<'_, TransactionState>, TransactionOverlayError> {
self.state.lock().map_err(TransactionOverlayError::poisoned)
}
}
impl<S> Store for OwnedTransactionOverlayStore<S>
where
S: Store,
{
type Error = TransactionOverlayError;
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
let staged = self.lock()?.node_writes.get(key).cloned();
match staged {
Some(value) => Ok(value),
None => self.base.get(key).map_err(TransactionOverlayError::store),
}
}
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Self::Error> {
self.lock()?
.node_writes
.insert(key.to_vec(), Some(value.to_vec()));
Ok(())
}
fn delete(&self, key: &[u8]) -> Result<(), Self::Error> {
self.lock()?.node_writes.insert(key.to_vec(), None);
Ok(())
}
fn batch(&self, ops: &[BatchOp]) -> Result<(), Self::Error> {
let mut state = self.lock()?;
for op in ops {
match op {
BatchOp::Upsert { key, value } => {
state
.node_writes
.insert((*key).to_vec(), Some((*value).to_vec()));
}
BatchOp::Delete { key } => {
state.node_writes.insert((*key).to_vec(), None);
}
}
}
Ok(())
}
}
impl<S> ManifestStore for OwnedTransactionOverlayStore<S>
where
S: Store + ManifestStore,
{
type Error = TransactionOverlayError;
fn get_root(&self, name: &[u8]) -> Result<Option<RootManifest>, Self::Error> {
if let Some(write) = self.lock()?.root_writes.get(name).cloned() {
return Ok(write.replacement().cloned());
}
let current = self
.base
.get_root(name)
.map_err(TransactionOverlayError::store)?;
let mut state = self.lock()?;
state
.root_reads
.entry(name.to_vec())
.or_insert_with(|| current.clone());
Ok(current)
}
fn put_root(&self, name: &[u8], manifest: &RootManifest) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Put {
name: name.to_vec(),
manifest: manifest.clone(),
},
);
Ok(())
}
fn delete_root(&self, name: &[u8]) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Delete {
name: name.to_vec(),
},
);
Ok(())
}
fn compare_and_swap_root(
&self,
name: &[u8],
expected: Option<&RootManifest>,
new: Option<&RootManifest>,
) -> Result<ManifestUpdate, Self::Error> {
let current = self.get_root(name)?;
if current.as_ref() != expected {
return Ok(ManifestUpdate::Conflict { current });
}
match new {
Some(manifest) => self.put_root(name, manifest)?,
None => self.delete_root(name)?,
}
Ok(ManifestUpdate::Applied)
}
}
#[cfg(feature = "async-store")]
#[derive(Clone)]
pub struct AsyncTransactionOverlayStore<'a, S> {
base: &'a S,
state: Arc<Mutex<TransactionState>>,
}
#[cfg(feature = "async-store")]
impl<'a, S> AsyncTransactionOverlayStore<'a, S> {
fn new(base: &'a S, state: Arc<Mutex<TransactionState>>) -> Self {
Self { base, state }
}
fn lock(&self) -> Result<MutexGuard<'_, TransactionState>, TransactionOverlayError> {
self.state.lock().map_err(TransactionOverlayError::poisoned)
}
}
#[cfg(feature = "async-store")]
impl<S> AsyncStore for AsyncTransactionOverlayStore<'_, S>
where
S: AsyncStore,
S::Error: Send + Sync,
{
type Error = TransactionOverlayError;
async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
let staged = self.lock()?.node_writes.get(key).cloned();
match staged {
Some(value) => Ok(value),
None => self
.base
.get(key)
.await
.map_err(TransactionOverlayError::store),
}
}
async fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Self::Error> {
self.lock()?
.node_writes
.insert(key.to_vec(), Some(value.to_vec()));
Ok(())
}
async fn delete(&self, key: &[u8]) -> Result<(), Self::Error> {
self.lock()?.node_writes.insert(key.to_vec(), None);
Ok(())
}
async fn batch(&self, ops: &[BatchOp<'_>]) -> Result<(), Self::Error> {
let mut state = self.lock()?;
for op in ops {
match op {
BatchOp::Upsert { key, value } => {
state
.node_writes
.insert((*key).to_vec(), Some((*value).to_vec()));
}
BatchOp::Delete { key } => {
state.node_writes.insert((*key).to_vec(), None);
}
}
}
Ok(())
}
}
#[cfg(feature = "async-store")]
impl<S> AsyncManifestStore for AsyncTransactionOverlayStore<'_, S>
where
S: AsyncStore + AsyncManifestStore,
<S as AsyncManifestStore>::Error: Send + Sync,
{
type Error = TransactionOverlayError;
async fn get_root(&self, name: &[u8]) -> Result<Option<RootManifest>, Self::Error> {
if let Some(write) = self.lock()?.root_writes.get(name).cloned() {
return Ok(write.replacement().cloned());
}
let current = self
.base
.get_root(name)
.await
.map_err(TransactionOverlayError::store)?;
let mut state = self.lock()?;
state
.root_reads
.entry(name.to_vec())
.or_insert_with(|| current.clone());
Ok(current)
}
async fn put_root(&self, name: &[u8], manifest: &RootManifest) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Put {
name: name.to_vec(),
manifest: manifest.clone(),
},
);
Ok(())
}
async fn delete_root(&self, name: &[u8]) -> Result<(), Self::Error> {
self.lock()?.root_writes.insert(
name.to_vec(),
RootWrite::Delete {
name: name.to_vec(),
},
);
Ok(())
}
async fn compare_and_swap_root(
&self,
name: &[u8],
expected: Option<&RootManifest>,
new: Option<&RootManifest>,
) -> Result<ManifestUpdate, Self::Error> {
let current = self.get_root(name).await?;
if current.as_ref() != expected {
return Ok(ManifestUpdate::Conflict { current });
}
match new {
Some(manifest) => self.put_root(name, manifest).await?,
None => self.delete_root(name).await?,
}
Ok(ManifestUpdate::Applied)
}
}
pub struct ProllyTransaction<'a, S>
where
S: Store + ManifestStore + TransactionalStore,
{
base: &'a Prolly<S>,
state: Arc<Mutex<TransactionState>>,
manager: Prolly<TransactionOverlayStore<'a, S>>,
completed: bool,
}
impl<'a, S> ProllyTransaction<'a, S>
where
S: Store + ManifestStore + TransactionalStore,
{
fn new(base: &'a Prolly<S>) -> Result<Self, Error> {
if !base.store.supports_transactions() {
return Err(Error::UnsupportedTransactions {
store: type_name::<S>(),
});
}
let state = Arc::new(Mutex::new(TransactionState::default()));
let overlay = TransactionOverlayStore::new(&base.store, state.clone());
let manager = Prolly::new(overlay, base.config.clone());
Ok(Self {
base,
state,
manager,
completed: false,
})
}
pub fn create(&self) -> Tree {
self.manager.create()
}
pub fn get(&self, tree: &Tree, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
self.manager.get(tree, key)
}
pub fn put(&self, tree: &Tree, key: Vec<u8>, value: Vec<u8>) -> Result<Tree, Error> {
self.manager.put(tree, key, value)
}
pub fn delete(&self, tree: &Tree, key: &[u8]) -> Result<Tree, Error> {
self.manager.delete(tree, key)
}
pub fn batch(&self, tree: &Tree, mutations: Vec<Mutation>) -> Result<Tree, Error> {
self.manager.batch(tree, mutations)
}
pub fn load_named_root(&self, name: &[u8]) -> Result<Option<Tree>, Error> {
self.manager.load_named_root(name)
}
pub fn publish_named_root(&self, name: &[u8], tree: &Tree) -> Result<(), Error> {
self.manager.publish_named_root(name, tree)
}
pub fn delete_named_root(&self, name: &[u8]) -> Result<(), Error> {
self.manager.delete_named_root(name)
}
pub fn compare_and_swap_named_root(
&self,
name: &[u8],
expected: Option<&Tree>,
new: Option<&Tree>,
) -> Result<NamedRootUpdate, Error> {
self.manager
.compare_and_swap_named_root(name, expected, new)
}
pub fn rollback(mut self) {
self.completed = true;
}
pub fn commit(mut self) -> Result<TransactionUpdate, Error> {
let (node_writes, root_conditions, root_writes) = {
let state = self
.state
.lock()
.map_err(|err| Error::Store(Box::new(TransactionOverlayError::poisoned(err))))?;
(
state.node_writes(),
state.root_conditions(),
state.root_writes(),
)
};
let update =
self.base
.store
.commit_transaction(&node_writes, &root_conditions, &root_writes)?;
self.completed = true;
Ok(update)
}
}
pub struct OwnedProllyTransaction<S>
where
S: Store + ManifestStore + TransactionalStore,
{
base_store: S,
state: Arc<Mutex<TransactionState>>,
manager: Prolly<OwnedTransactionOverlayStore<S>>,
completed: bool,
}
impl<S> OwnedProllyTransaction<S>
where
S: Store + ManifestStore + TransactionalStore + Clone,
{
fn new(base: &Prolly<S>) -> Result<Self, Error> {
if !base.store.supports_transactions() {
return Err(Error::UnsupportedTransactions {
store: type_name::<S>(),
});
}
let base_store = base.store.clone();
let state = Arc::new(Mutex::new(TransactionState::default()));
let overlay = OwnedTransactionOverlayStore::new(base_store.clone(), state.clone());
let manager = Prolly::new(overlay, base.config.clone());
Ok(Self {
base_store,
state,
manager,
completed: false,
})
}
pub fn create(&self) -> Tree {
self.manager.create()
}
pub fn get(&self, tree: &Tree, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
self.manager.get(tree, key)
}
pub fn put(&self, tree: &Tree, key: Vec<u8>, value: Vec<u8>) -> Result<Tree, Error> {
self.manager.put(tree, key, value)
}
pub fn delete(&self, tree: &Tree, key: &[u8]) -> Result<Tree, Error> {
self.manager.delete(tree, key)
}
pub fn batch(&self, tree: &Tree, mutations: Vec<Mutation>) -> Result<Tree, Error> {
self.manager.batch(tree, mutations)
}
pub fn load_named_root(&self, name: &[u8]) -> Result<Option<Tree>, Error> {
self.manager.load_named_root(name)
}
pub fn publish_named_root(&self, name: &[u8], tree: &Tree) -> Result<(), Error> {
self.manager.publish_named_root(name, tree)
}
pub fn delete_named_root(&self, name: &[u8]) -> Result<(), Error> {
self.manager.delete_named_root(name)
}
pub fn compare_and_swap_named_root(
&self,
name: &[u8],
expected: Option<&Tree>,
new: Option<&Tree>,
) -> Result<NamedRootUpdate, Error> {
self.manager
.compare_and_swap_named_root(name, expected, new)
}
pub fn rollback(mut self) {
self.completed = true;
}
pub fn commit(mut self) -> Result<TransactionUpdate, Error> {
let (node_writes, root_conditions, root_writes) = {
let state = self
.state
.lock()
.map_err(|err| Error::Store(Box::new(TransactionOverlayError::poisoned(err))))?;
(
state.node_writes(),
state.root_conditions(),
state.root_writes(),
)
};
let update =
self.base_store
.commit_transaction(&node_writes, &root_conditions, &root_writes)?;
self.completed = true;
Ok(update)
}
}
#[cfg(feature = "async-store")]
pub struct AsyncProllyTransaction<'a, S>
where
S: AsyncStore + AsyncManifestStore + AsyncTransactionalStore,
<S as AsyncStore>::Error: Send + Sync,
<S as AsyncManifestStore>::Error: Send + Sync,
{
base: &'a AsyncProlly<S>,
state: Arc<Mutex<TransactionState>>,
manager: AsyncProlly<AsyncTransactionOverlayStore<'a, S>>,
completed: bool,
}
#[cfg(feature = "async-store")]
impl<'a, S> AsyncProllyTransaction<'a, S>
where
S: AsyncStore + AsyncManifestStore + AsyncTransactionalStore,
<S as AsyncStore>::Error: Send + Sync,
<S as AsyncManifestStore>::Error: Send + Sync,
{
fn new(base: &'a AsyncProlly<S>) -> Result<Self, Error> {
if !base.store.supports_transactions() {
return Err(Error::UnsupportedTransactions {
store: type_name::<S>(),
});
}
let state = Arc::new(Mutex::new(TransactionState::default()));
let overlay = AsyncTransactionOverlayStore::new(&base.store, state.clone());
let manager = AsyncProlly::new(overlay, base.config.clone());
Ok(Self {
base,
state,
manager,
completed: false,
})
}
pub fn create(&self) -> Tree {
self.manager.create()
}
pub async fn get(&self, tree: &Tree, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
self.manager.get(tree, key).await
}
pub async fn put(&self, tree: &Tree, key: Vec<u8>, value: Vec<u8>) -> Result<Tree, Error> {
self.manager.put(tree, key, value).await
}
pub async fn delete(&self, tree: &Tree, key: &[u8]) -> Result<Tree, Error> {
self.manager.delete(tree, key).await
}
pub async fn batch(&self, tree: &Tree, mutations: Vec<Mutation>) -> Result<Tree, Error> {
self.manager.batch(tree, mutations).await
}
pub async fn load_named_root(&self, name: &[u8]) -> Result<Option<Tree>, Error> {
self.manager.load_named_root(name).await
}
pub async fn publish_named_root(&self, name: &[u8], tree: &Tree) -> Result<(), Error> {
self.manager.publish_named_root(name, tree).await
}
pub async fn delete_named_root(&self, name: &[u8]) -> Result<(), Error> {
self.manager.delete_named_root(name).await
}
pub async fn compare_and_swap_named_root(
&self,
name: &[u8],
expected: Option<&Tree>,
new: Option<&Tree>,
) -> Result<NamedRootUpdate, Error> {
self.manager
.compare_and_swap_named_root(name, expected, new)
.await
}
pub fn rollback(mut self) {
self.completed = true;
}
pub async fn commit(mut self) -> Result<TransactionUpdate, Error> {
let (node_writes, root_conditions, root_writes) = {
let state = self
.state
.lock()
.map_err(|err| Error::Store(Box::new(TransactionOverlayError::poisoned(err))))?;
(
state.node_writes(),
state.root_conditions(),
state.root_writes(),
)
};
let update = self
.base
.store
.commit_transaction(&node_writes, &root_conditions, &root_writes)
.await?;
self.completed = true;
Ok(update)
}
}
#[cfg(feature = "async-store")]
impl<S> Drop for AsyncProllyTransaction<'_, S>
where
S: AsyncStore + AsyncManifestStore + AsyncTransactionalStore,
<S as AsyncStore>::Error: Send + Sync,
<S as AsyncManifestStore>::Error: Send + Sync,
{
fn drop(&mut self) {
if !self.completed {
self.completed = true;
}
}
}
impl<S> Drop for ProllyTransaction<'_, S>
where
S: Store + ManifestStore + TransactionalStore,
{
fn drop(&mut self) {
if !self.completed {
self.completed = true;
}
}
}
impl<S> Drop for OwnedProllyTransaction<S>
where
S: Store + ManifestStore + TransactionalStore,
{
fn drop(&mut self) {
if !self.completed {
self.completed = true;
}
}
}
impl<S> Prolly<S>
where
S: Store + ManifestStore + TransactionalStore,
{
pub fn begin_transaction(&self) -> Result<ProllyTransaction<'_, S>, Error> {
ProllyTransaction::new(self)
}
pub fn begin_owned_transaction(&self) -> Result<OwnedProllyTransaction<S>, Error>
where
S: Clone,
{
OwnedProllyTransaction::new(self)
}
pub fn transaction<T>(
&self,
f: impl FnOnce(&mut ProllyTransaction<'_, S>) -> Result<T, Error>,
) -> Result<T, Error> {
let mut tx = self.begin_transaction()?;
let value = f(&mut tx)?;
match tx.commit()? {
TransactionUpdate::Applied { .. } => Ok(value),
TransactionUpdate::Conflict(conflict) => Err(Error::TransactionConflict(conflict)),
}
}
}
#[cfg(feature = "async-store")]
impl<S> AsyncProlly<S>
where
S: AsyncStore + AsyncManifestStore + AsyncTransactionalStore,
<S as AsyncStore>::Error: Send + Sync,
<S as AsyncManifestStore>::Error: Send + Sync,
{
pub fn begin_transaction(&self) -> Result<AsyncProllyTransaction<'_, S>, Error> {
AsyncProllyTransaction::new(self)
}
pub async fn transaction<T, F>(&self, f: F) -> Result<T, Error>
where
F: for<'tx> FnOnce(
&'tx mut AsyncProllyTransaction<'_, S>,
) -> Pin<Box<dyn Future<Output = Result<T, Error>> + 'tx>>,
{
let mut tx = self.begin_transaction()?;
let value = f(&mut tx).await?;
match tx.commit().await? {
TransactionUpdate::Applied { .. } => Ok(value),
TransactionUpdate::Conflict(conflict) => Err(Error::TransactionConflict(conflict)),
}
}
}