Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/storage/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ pub trait StorageWriteBatch: Send {
/// Removes a key-value pair from the specified table.
fn delete(&mut self, table: &'static str, key: &[u8]) -> Result<(), StoreError>;

/// Removes every key in `[start, end)` from the specified table.
///
/// Half-open range; `end` is exclusive. Equivalent to enumerating each key
/// in the range and calling [`delete`], but backends with native range-delete
/// support (e.g. RocksDB's `delete_range_cf`) can implement it more efficiently.
///
/// Lexicographic byte order is used for the range bounds — callers using
/// numeric keys must encode them in a representation whose lex order matches
/// numeric order (e.g. `u64::to_be_bytes()`).
fn delete_range(
&mut self,
table: &'static str,
start: &[u8],
end: &[u8],
) -> Result<(), StoreError>;

/// Commits all changes made in this transaction.
fn commit(&mut self) -> Result<(), StoreError>;
}
Expand Down
11 changes: 10 additions & 1 deletion crates/storage/api/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ pub const STORAGE_FLATKEYVALUE: &str = "storage_flatkeyvalue";

pub const MISC_VALUES: &str = "misc_values";

/// State-history journal column family: [`u8; 8`] => [`Vec<u8>`]
/// - [`u8; 8`] = `block_number.to_be_bytes()` (big-endian so lex order == numeric order)
/// - [`Vec<u8>`] = `JournalEntry::encode()`
///
/// Stores one reverse-diff entry per committed block, enabling reorgs deeper
/// than the in-memory `TrieLayerCache`. Pruned at finality.
pub const STATE_HISTORY: &str = "state_history";

/// Execution witnesses column family: [`Vec<u8>`] => [`Vec<u8>`]
/// - [`Vec<u8>`] = Composite key
/// ```rust,no_run
Expand All @@ -114,7 +122,7 @@ pub const EXECUTION_WITNESSES: &str = "execution_witnesses";
/// - [`Vec<u8>`] = RLP-encoded `BlockAccessList`
pub const BLOCK_ACCESS_LISTS: &str = "block_access_lists";

pub const TABLES: [&str; 20] = [
pub const TABLES: [&str; 21] = [
CHAIN_DATA,
ACCOUNT_CODES,
ACCOUNT_CODE_METADATA,
Expand All @@ -135,4 +143,5 @@ pub const TABLES: [&str; 20] = [
MISC_VALUES,
EXECUTION_WITNESSES,
BLOCK_ACCESS_LISTS,
STATE_HISTORY,
];
25 changes: 24 additions & 1 deletion crates/storage/backend/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,31 @@ impl StorageWriteBatch for InMemoryWriteTx {
Ok(())
}

fn delete_range(
&mut self,
table: &'static str,
start: &[u8],
end: &[u8],
) -> Result<(), StoreError> {
let mut db = self
.backend
.write()
.map_err(|_| StoreError::Custom("Failed to acquire write lock".to_string()))?;

let db_mut = Arc::make_mut(&mut *db);
if let Some(table_ref) = db_mut.get_mut(table) {
table_ref.retain(|k, _| !(k.as_slice() >= start && k.as_slice() < end));
}
Ok(())
}

fn commit(&mut self) -> Result<(), StoreError> {
// FIXME: in-memory writes aren't atomic
// FIXME: in-memory writes aren't atomic. Every `put`, `delete`, and
// `delete_range` above mutates the live `Arc<Database>` immediately under
// the write lock; `commit` is a no-op. Anyone using this backend cannot
// rely on multi-op atomicity (e.g. the journal entry + trie writes in
// `apply_trie_updates`, or the `delete_range` + finalized-number update
// in `forkchoice_update_inner`).
Ok(())
}
}
14 changes: 14 additions & 0 deletions crates/storage/backend/rocksdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,20 @@ impl StorageWriteBatch for RocksDBWriteTx {
Ok(())
}

fn delete_range(
&mut self,
table: &'static str,
start: &[u8],
end: &[u8],
) -> Result<(), StoreError> {
let cf = self
.db
.cf_handle(table)
.ok_or_else(|| StoreError::Custom(format!("Table {table:?} not found")))?;
self.batch.delete_range_cf(&cf, start, end);
Ok(())
}

fn commit(&mut self) -> Result<(), StoreError> {
// Take ownership of the batch (replaces it with an empty one) since db.write() consumes it
let batch = std::mem::take(&mut self.batch);
Expand Down
Loading
Loading