feat(chain): reintroduce a way to stage changesets before persisting
A staging area is helpful because we can contain logic to ignore empty changesets and not clear staging area if the persistence backend fails.
This commit is contained in:
parent
36e82ec686
commit
2e40b0118c
@ -11,6 +11,8 @@ use async_trait::async_trait;
|
|||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::fmt::{Debug, Display};
|
use core::fmt::{Debug, Display};
|
||||||
|
|
||||||
|
use crate::Append;
|
||||||
|
|
||||||
/// A changeset containing [`crate`] structures typically persisted together.
|
/// A changeset containing [`crate`] structures typically persisted together.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg(feature = "miniscript")]
|
#[cfg(feature = "miniscript")]
|
||||||
@ -88,6 +90,19 @@ impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "miniscript")]
|
||||||
|
impl<K, A> From<crate::keychain::ChangeSet<K>> for CombinedChangeSet<K, A> {
|
||||||
|
fn from(indexer: crate::keychain::ChangeSet<K>) -> Self {
|
||||||
|
Self {
|
||||||
|
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet {
|
||||||
|
indexer,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A persistence backend for writing and loading changesets.
|
/// A persistence backend for writing and loading changesets.
|
||||||
///
|
///
|
||||||
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
||||||
@ -167,3 +182,98 @@ impl<C> PersistBackendAsync<C> for () {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extends a changeset so that it acts as a convenient staging area for any [`PersistBackend`].
|
||||||
|
///
|
||||||
|
/// Not all changes to the in-memory representation needs to be written to disk right away.
|
||||||
|
/// [`Append::append`] can be used to *stage* changes first and then [`StageExt::commit_to`] can be
|
||||||
|
/// used to write changes to disk.
|
||||||
|
pub trait StageExt: Append + Default + Sized {
|
||||||
|
/// Commit the staged changes to the persistence `backend`.
|
||||||
|
///
|
||||||
|
/// Changes that are committed (if any) are returned.
|
||||||
|
///
|
||||||
|
/// # Error
|
||||||
|
///
|
||||||
|
/// Returns a backend-defined error if this fails.
|
||||||
|
fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
|
||||||
|
where
|
||||||
|
B: PersistBackend<Self>,
|
||||||
|
{
|
||||||
|
// do not do anything if changeset is empty
|
||||||
|
if self.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
backend.write_changes(&*self)?;
|
||||||
|
// only clear if changes are written successfully to backend
|
||||||
|
Ok(Some(core::mem::take(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
|
||||||
|
/// the persistence `backend`.
|
||||||
|
///
|
||||||
|
/// Convenience method for calling [`Append::append`] and then [`StageExt::commit_to`].
|
||||||
|
fn append_and_commit_to<B>(
|
||||||
|
&mut self,
|
||||||
|
changeset: Self,
|
||||||
|
backend: &mut B,
|
||||||
|
) -> Result<Option<Self>, B::WriteError>
|
||||||
|
where
|
||||||
|
B: PersistBackend<Self>,
|
||||||
|
{
|
||||||
|
Append::append(self, changeset);
|
||||||
|
self.commit_to(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Append + Default> StageExt for C {}
|
||||||
|
|
||||||
|
/// Extends a changeset so that it acts as a convenient staging area for any
|
||||||
|
/// [`PersistBackendAsync`].
|
||||||
|
///
|
||||||
|
/// Not all changes to the in-memory representation needs to be written to disk right away.
|
||||||
|
/// [`Append::append`] can be used to *stage* changes first and then [`StageExtAsync::commit_to`]
|
||||||
|
/// can be used to write changes to disk.
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[async_trait]
|
||||||
|
pub trait StageExtAsync: Append + Default + Sized + Send + Sync {
|
||||||
|
/// Commit the staged changes to the persistence `backend`.
|
||||||
|
///
|
||||||
|
/// Changes that are committed (if any) are returned.
|
||||||
|
///
|
||||||
|
/// # Error
|
||||||
|
///
|
||||||
|
/// Returns a backend-defined error if this fails.
|
||||||
|
async fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
|
||||||
|
where
|
||||||
|
B: PersistBackendAsync<Self> + Send + Sync,
|
||||||
|
{
|
||||||
|
// do not do anything if changeset is empty
|
||||||
|
if self.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
backend.write_changes(&*self).await?;
|
||||||
|
// only clear if changes are written successfully to backend
|
||||||
|
Ok(Some(core::mem::take(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
|
||||||
|
/// the persistence `backend`.
|
||||||
|
///
|
||||||
|
/// Convenience method for calling [`Append::append`] and then [`StageExtAsync::commit_to`].
|
||||||
|
async fn append_and_commit_to<B>(
|
||||||
|
&mut self,
|
||||||
|
changeset: Self,
|
||||||
|
backend: &mut B,
|
||||||
|
) -> Result<Option<Self>, B::WriteError>
|
||||||
|
where
|
||||||
|
B: PersistBackendAsync<Self> + Send + Sync,
|
||||||
|
{
|
||||||
|
Append::append(self, changeset);
|
||||||
|
self.commit_to(backend).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
#[async_trait]
|
||||||
|
impl<C: Append + Default + Send + Sync> StageExtAsync for C {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user