Skip to content

Commit c1b3e0c

Browse files
committed
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.
1 parent 1acaa37 commit c1b3e0c

File tree

1 file changed

+97
-1
lines changed

1 file changed

+97
-1
lines changed

crates/chain/src/persist.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use async_trait::async_trait;
1111
use core::convert::Infallible;
1212
use core::fmt::{Debug, Display};
1313

14+
use crate::Append;
15+
1416
/// A changeset containing [`crate`] structures typically persisted together.
1517
#[derive(Debug, Clone, PartialEq)]
1618
#[cfg(feature = "miniscript")]
@@ -87,7 +89,6 @@ impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet
8789
}
8890
}
8991
}
90-
9192
/// A persistence backend for writing and loading changesets.
9293
///
9394
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
@@ -167,3 +168,98 @@ impl<C> PersistBackendAsync<C> for () {
167168
Ok(None)
168169
}
169170
}
171+
172+
/// Extends a changeset so that it acts as a convenient staging area for any [`PersistBackend`].
173+
///
174+
/// Not all changes to the in-memory representation needs to be written to disk right away.
175+
/// [`Append::append`] can be used to *stage* changes first and then [`StageExt::commit_to`] can be
176+
/// used to write changes to disk.
177+
pub trait StageExt: Append + Default + Sized {
178+
/// Commit the staged changes to the persistence `backend`.
179+
///
180+
/// Changes that are committed (if any) are returned.
181+
///
182+
/// # Error
183+
///
184+
/// Returns a backend-defined error if this fails.
185+
fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
186+
where
187+
B: PersistBackend<Self>,
188+
{
189+
// do not do anything if changeset is empty
190+
if self.is_empty() {
191+
return Ok(None);
192+
}
193+
backend.write_changes(&*self)?;
194+
// only clear if changes are written successfully to backend
195+
Ok(Some(core::mem::take(self)))
196+
}
197+
198+
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
199+
/// the persistence `backend`.
200+
///
201+
/// Convenience method for calling [`Append::append`] and then [`StageExt::commit_to`].
202+
fn append_and_commit_to<B>(
203+
&mut self,
204+
changeset: Self,
205+
backend: &mut B,
206+
) -> Result<Option<Self>, B::WriteError>
207+
where
208+
B: PersistBackend<Self>,
209+
{
210+
Append::append(self, changeset);
211+
self.commit_to(backend)
212+
}
213+
}
214+
215+
impl<C: Append + Default> StageExt for C {}
216+
217+
/// Extends a changeset so that it acts as a convenient staging area for any
218+
/// [`PersistBackendAsync`].
219+
///
220+
/// Not all changes to the in-memory representation needs to be written to disk right away.
221+
/// [`Append::append`] can be used to *stage* changes first and then [`StageExtAsync::commit_to`]
222+
/// can be used to write changes to disk.
223+
#[cfg(feature = "async")]
224+
#[async_trait]
225+
pub trait StageExtAsync: Append + Default + Sized + Send + Sync {
226+
/// Commit the staged changes to the persistence `backend`.
227+
///
228+
/// Changes that are committed (if any) are returned.
229+
///
230+
/// # Error
231+
///
232+
/// Returns a backend-defined error if this fails.
233+
async fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
234+
where
235+
B: PersistBackendAsync<Self> + Send + Sync,
236+
{
237+
// do not do anything if changeset is empty
238+
if self.is_empty() {
239+
return Ok(None);
240+
}
241+
backend.write_changes(&*self).await?;
242+
// only clear if changes are written successfully to backend
243+
Ok(Some(core::mem::take(self)))
244+
}
245+
246+
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
247+
/// the persistence `backend`.
248+
///
249+
/// Convenience method for calling [`Append::append`] and then [`StageExtAsync::commit_to`].
250+
async fn append_and_commit_to<B>(
251+
&mut self,
252+
changeset: Self,
253+
backend: &mut B,
254+
) -> Result<Option<Self>, B::WriteError>
255+
where
256+
B: PersistBackendAsync<Self> + Send + Sync,
257+
{
258+
Append::append(self, changeset);
259+
self.commit_to(backend).await
260+
}
261+
}
262+
263+
#[cfg(feature = "async")]
264+
#[async_trait]
265+
impl<C: Append + Default + Send + Sync> StageExtAsync for C {}

0 commit comments

Comments
 (0)