Skip to content

Commit c356643

Browse files
committed
feat(reconstruction): Add mutable construtors
1 parent 17c06e7 commit c356643

File tree

2 files changed

+68
-7
lines changed

2 files changed

+68
-7
lines changed

firewood/src/db.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::manager::{ConfigManager, RevisionManager, RevisionManagerConfig};
2121
use firewood_metrics::firewood_counter;
2222
use firewood_storage::{
2323
CheckOpt, CheckerReport, Committed, FileBacked, FileIoError, HashedNodeReader,
24-
ImmutableProposal, NodeHashAlgorithm, NodeStore, Parentable, ReadableStorage,
24+
ImmutableProposal, MutableProposal, NodeHashAlgorithm, NodeStore, Parentable, ReadableStorage,
2525
Reconstructed as StorageReconstructed, TrieReader,
2626
};
2727
use nonzero_ext::nonzero;
@@ -488,9 +488,7 @@ impl api::Reconstructible for &NodeStore<Committed, FileBacked> {
488488
where
489489
Self: Sized,
490490
{
491-
let _ = batch.into_iter();
492-
let _ = self;
493-
todo!("historical reconstruction pipeline is not implemented yet")
491+
reconstruct_with_parent(self, batch)
494492
}
495493
}
496494

@@ -501,12 +499,41 @@ impl api::Reconstructible for Reconstructed {
501499
where
502500
Self: Sized,
503501
{
504-
let _ = batch.into_iter();
505-
let _ = self;
506-
todo!("linear reconstructed->reconstructed pipeline is not implemented yet")
502+
reconstruct_with_parent(&*self.nodestore, batch)
507503
}
508504
}
509505

506+
fn reconstruct_with_parent<P>(
507+
parent: &NodeStore<P, FileBacked>,
508+
batch: impl IntoBatchIter,
509+
) -> Result<Reconstructed, api::Error>
510+
where
511+
NodeStore<P, FileBacked>: TrieReader + HashedNodeReader,
512+
{
513+
let proposal = NodeStore::<MutableProposal, _>::new_for_reconstruction(parent)?;
514+
let mut merkle = Merkle::from(proposal);
515+
516+
for res in batch.into_iter().into_batch_iter::<api::Error>() {
517+
match res? {
518+
BatchOp::Put { key, value } => {
519+
merkle.insert(key.as_ref(), value.as_ref().into())?;
520+
}
521+
BatchOp::Delete { key } => {
522+
merkle.remove(key.as_ref())?;
523+
}
524+
BatchOp::DeleteRange { prefix } => {
525+
merkle.remove_prefix(prefix.as_ref())?;
526+
}
527+
}
528+
}
529+
530+
let nodestore = merkle.into_inner();
531+
let reconstructed = Arc::new(nodestore.try_into()?);
532+
Ok(Reconstructed {
533+
nodestore: reconstructed,
534+
})
535+
}
536+
510537
#[cfg(test)]
511538
mod test {
512539
#![expect(clippy::unwrap_used)]

storage/src/nodestore/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,40 @@ impl<S: ReadableStorage> NodeStore<MutableProposal, S> {
282282
})
283283
}
284284

285+
/// Create a new mutable nodestore for reconstruction from a read-capable parent.
286+
///
287+
/// Unlike [`NodeStore::new`], this constructor does not require `Parentable`.
288+
/// It is intended for linear reconstruction flows (e.g. historical or reconstructed views)
289+
/// that should not participate in proposal parent/reparent semantics.
290+
///
291+
/// # Errors
292+
///
293+
/// Returns a [`FileIoError`] if the parent root cannot be read.
294+
pub fn new_for_reconstruction<T>(parent: &NodeStore<T, S>) -> Result<Self, FileIoError>
295+
where
296+
NodeStore<T, S>: TrieReader + HashedNodeReader,
297+
{
298+
let mut deleted = Vec::default();
299+
let root = if let Some(root) = parent.root_as_maybe_persisted_node() {
300+
deleted.push(root.clone());
301+
let root = root.as_shared_node(parent)?.deref().clone();
302+
Some(root)
303+
} else {
304+
None
305+
};
306+
307+
let kind = MutableProposal {
308+
root,
309+
deleted,
310+
parent: NodeStoreParent::Committed(parent.root_hash()),
311+
};
312+
313+
Ok(NodeStore {
314+
kind,
315+
storage: parent.storage.clone(),
316+
})
317+
}
318+
285319
/// Marks the node at `addr` as deleted in this proposal.
286320
pub fn delete_node(&mut self, node: MaybePersistedNode) {
287321
trace!("Pending delete at {node:?}");

0 commit comments

Comments
 (0)