Skip to content

Commit bbc6118

Browse files
committed
chore: improve comments and docs
1 parent 8e22355 commit bbc6118

7 files changed

Lines changed: 89 additions & 23 deletions

File tree

crates/storage/src/cold/traits.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,30 @@ impl BlockData {
5353
/// The trait is agnostic to how the backend stores or indexes data.
5454
///
5555
/// All methods are async and return futures that are `Send`.
56+
///
57+
/// # Implementation Guide
58+
///
59+
/// Implementers must ensure:
60+
///
61+
/// - **Append-only ordering**: `append_block` must enforce monotonically
62+
/// increasing block numbers. Attempting to append a block with a number <=
63+
/// the current latest should return an error.
64+
///
65+
/// - **Atomic truncation**: `truncate_above` must remove all data for blocks
66+
/// N+1 and higher atomically. Partial truncation is not acceptable.
67+
///
68+
/// - **Index maintenance**: Hash-based lookups (e.g., header by hash,
69+
/// transaction by hash) require the implementation to maintain appropriate
70+
/// indexes. These indexes must be updated during `append_block` and cleaned
71+
/// during `truncate_above`.
72+
///
73+
/// - **Consistent reads**: Read operations should return consistent snapshots.
74+
/// A read started before a write completes should not see partial data from
75+
/// that write.
76+
///
77+
/// - **Tag resolution**: `HeaderSpecifier::Tag` variants (Latest, Finalized,
78+
/// Safe, Earliest) must be resolved by the implementation. For simple
79+
/// backends, Latest/Finalized/Safe may all resolve to the same block.
5680
pub trait ColdStorage: Send + Sync + 'static {
5781
// --- Headers ---
5882

crates/storage/src/hot/impls/mem.rs

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -490,20 +490,27 @@ impl<'a> MemKvCursorMut<'a> {
490490
}
491491
}
492492

493-
/// Get the first key-value pair >= key, returning owned data
493+
/// Get the first key-value pair >= key, returning owned data.
494+
///
495+
/// Merges queued operations with committed data, giving precedence to queued
496+
/// ops for read-your-writes consistency.
494497
fn get_range_owned(&self, key: &MemStoreKey) -> Option<(MemStoreKey, Bytes)> {
498+
// Find the first candidate from both queued ops and committed storage.
495499
let q = self.queued_ops.range(*key..).next();
496500
let c = if !self.is_cleared { self.table.range(*key..).next() } else { None };
497501

498502
match (q, c) {
499503
(None, None) => None,
504+
505+
// Both sources have candidates - pick the smaller key, preferring
506+
// queued ops on ties for read-your-writes consistency.
500507
(Some((qk, queued)), Some((ck, current))) => {
501508
if qk <= ck {
502-
// Queued operation takes precedence
503509
match queued {
504510
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
505511
QueuedKvOp::Delete => {
506-
// Skip deleted entry and look for next
512+
// This key is marked deleted; increment to skip it
513+
// and recurse to find the next valid entry.
507514
let mut next_key = *qk;
508515
for i in (0..next_key.len()).rev() {
509516
if next_key[i] < u8::MAX {
@@ -519,9 +526,12 @@ impl<'a> MemKvCursorMut<'a> {
519526
Some((*ck, current.clone()))
520527
}
521528
}
529+
530+
// Only queued ops have a candidate.
522531
(Some((qk, queued)), None) => match queued {
523532
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
524533
QueuedKvOp::Delete => {
534+
// Increment past the deleted key and recurse.
525535
let mut next_key = *qk;
526536
for i in (0..next_key.len()).rev() {
527537
if next_key[i] < u8::MAX {
@@ -533,14 +543,20 @@ impl<'a> MemKvCursorMut<'a> {
533543
self.get_range_owned(&next_key)
534544
}
535545
},
546+
547+
// Only committed storage has a candidate.
536548
(None, Some((ck, current))) => Some((*ck, current.clone())),
537549
}
538550
}
539551

540-
/// Get the first key-value pair > key (strictly greater), returning owned data
552+
/// Get the first key-value pair > key (strictly greater), returning owned data.
553+
///
554+
/// Similar to `get_range_owned` but uses exclusive bounds for cursor
555+
/// navigation (read_next).
541556
fn get_range_exclusive_owned(&self, key: &MemStoreKey) -> Option<(MemStoreKey, Bytes)> {
542557
use core::ops::Bound;
543558

559+
// Find candidates strictly greater than the given key.
544560
let q = self.queued_ops.range((Bound::Excluded(*key), Bound::Unbounded)).next();
545561
let c = if !self.is_cleared {
546562
self.table.range((Bound::Excluded(*key), Bound::Unbounded)).next()
@@ -550,31 +566,33 @@ impl<'a> MemKvCursorMut<'a> {
550566

551567
match (q, c) {
552568
(None, None) => None,
569+
570+
// Both sources have candidates.
553571
(Some((qk, queued)), Some((ck, current))) => {
554572
if qk <= ck {
555-
// Queued operation takes precedence
556573
match queued {
557574
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
558-
QueuedKvOp::Delete => {
559-
// This key is deleted, recurse to find the next one
560-
self.get_range_exclusive_owned(qk)
561-
}
575+
// Deleted in queue; skip and recurse.
576+
QueuedKvOp::Delete => self.get_range_exclusive_owned(qk),
562577
}
563578
} else {
564-
// Check if the current key has a delete queued
579+
// Committed key is smaller, but check if it's been deleted.
565580
if let Some(QueuedKvOp::Delete) = self.queued_ops.get(ck) {
566581
self.get_range_exclusive_owned(ck)
567582
} else {
568583
Some((*ck, current.clone()))
569584
}
570585
}
571586
}
587+
588+
// Only queued ops have a candidate.
572589
(Some((qk, queued)), None) => match queued {
573590
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
574591
QueuedKvOp::Delete => self.get_range_exclusive_owned(qk),
575592
},
593+
594+
// Only committed storage has a candidate; verify not deleted.
576595
(None, Some((ck, current))) => {
577-
// Check if the current key has a delete queued
578596
if let Some(QueuedKvOp::Delete) = self.queued_ops.get(ck) {
579597
self.get_range_exclusive_owned(ck)
580598
} else {
@@ -584,28 +602,39 @@ impl<'a> MemKvCursorMut<'a> {
584602
}
585603
}
586604

587-
/// Get the last key-value pair < key, returning owned data
605+
/// Get the last key-value pair < key, returning owned data.
606+
///
607+
/// Reverse iteration for cursor navigation (read_prev). Merges queued ops
608+
/// with committed data, preferring the larger key (closest to search key).
588609
fn get_range_reverse_owned(&self, key: &MemStoreKey) -> Option<(MemStoreKey, Bytes)> {
610+
// Find candidates strictly less than the given key, scanning backwards.
589611
let q = self.queued_ops.range(..*key).next_back();
590612
let c = if !self.is_cleared { self.table.range(..*key).next_back() } else { None };
591613

592614
match (q, c) {
593615
(None, None) => None,
616+
617+
// Both sources have candidates - pick the larger key (closest to
618+
// search position), preferring queued ops on ties.
594619
(Some((qk, queued)), Some((ck, current))) => {
595620
if qk >= ck {
596-
// Queued operation takes precedence
597621
match queued {
598622
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
623+
// Deleted; recurse to find the previous valid entry.
599624
QueuedKvOp::Delete => self.get_range_reverse_owned(qk),
600625
}
601626
} else {
602627
Some((*ck, current.clone()))
603628
}
604629
}
630+
631+
// Only queued ops have a candidate.
605632
(Some((qk, queued)), None) => match queued {
606633
QueuedKvOp::Put { value } => Some((*qk, value.clone())),
607634
QueuedKvOp::Delete => self.get_range_reverse_owned(qk),
608635
},
636+
637+
// Only committed storage has a candidate.
609638
(None, Some((ck, current))) => Some((*ck, current.clone())),
610639
}
611640
}

crates/storage/src/hot/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! data. It provides abstractions and implementations for key-value storage
55
//! backends.
66
//!
7-
//! ## Serialiazation
7+
//! ## Serialization
88
//!
99
//! Hot storage is opinionated with respect to serialization. Each table defines
1010
//! the key and value types it uses, and these types must implement the

crates/storage/src/hot/model/db_traits.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ use reth_db::{BlockNumberList, models::BlockNumberAddress};
88
use reth_db_api::models::ShardedKey;
99

1010
/// Trait for database read operations on standard hot tables.
11+
///
12+
/// This is a high-level trait that provides convenient methods for reading
13+
/// common data types from predefined hot storage tables. It builds upon the
14+
/// lower-level [`HotKvRead`] trait, which provides raw key-value access.
15+
///
16+
/// Users should prefer this trait unless customizations are needed to the
17+
/// table set.
1118
pub trait HotDbRead: HotKvRead + sealed::Sealed {
1219
/// Read a block header by its number.
1320
fn get_header(&self, number: u64) -> Result<Option<Header>, Self::Error> {
@@ -121,6 +128,14 @@ impl<T> HotDbWrite for T where T: HotKvWrite {}
121128
/// These tables maintain historical information about accounts and storage
122129
/// changes, and their contents can be used to reconstruct past states or
123130
/// roll back changes.
131+
///
132+
/// This is a high-level trait that provides convenient methods for reading
133+
/// common data types from predefined hot storage history tables. It builds
134+
/// upon the lower-level [`HotDbRead`] trait, which provides raw key-value
135+
/// access.
136+
///
137+
/// Users should prefer this trait unless customizations are needed to the
138+
/// table set.
124139
pub trait HotHistoryRead: HotDbRead {
125140
/// Get the list of block numbers where an account was touched.
126141
/// Get the list of block numbers where an account was touched.

crates/storage/src/hot/model/revm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ where
435435
}
436436
}
437437

438-
#[cfg(test)]
438+
#[cfg(all(test, feature = "in-mem"))]
439439
mod tests {
440440
use super::*;
441441
use crate::hot::{

crates/storage/src/hot/model/traits.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ pub trait HotKv {
6262
}
6363

6464
/// Trait for hot storage read transactions.
65+
///
66+
/// This trait provides read-only access to hot storage tables. It should only
67+
/// be imported if accessing custom tables, or when implementing new hot storage
68+
/// backends.
6569
#[auto_impl::auto_impl(&, Arc, Box)]
6670
pub trait HotKvRead {
6771
/// Error type for read operations.

crates/storage/src/hot/ser/traits.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,7 @@ pub trait KeySer: PartialOrd + Ord + Sized + Clone + core::fmt::Debug {
5555
/// Useful in DB decoding, where the absence of a key is represented by
5656
/// `None`.
5757
fn maybe_decode_key(data: Option<&[u8]>) -> Result<Option<Self>, DeserError> {
58-
match data {
59-
Some(d) => Ok(Some(Self::decode_key(d)?)),
60-
None => Ok(None),
61-
}
58+
data.map(Self::decode_key).transpose()
6259
}
6360
}
6461

@@ -105,10 +102,7 @@ pub trait ValSer {
105102
where
106103
Self: Sized,
107104
{
108-
match data {
109-
Some(d) => Ok(Some(Self::decode_value(d)?)),
110-
None => Ok(None),
111-
}
105+
data.map(Self::decode_value).transpose()
112106
}
113107

114108
/// Deserialize the value from bytes, ensuring all bytes are consumed.

0 commit comments

Comments
 (0)