ethrex_storage/api/mod.rs
1//! # Storage Backend API
2//!
3//! This module provides a thin, minimal interface for storage backends:
4//!
5//! - Thin: Minimal set of operations that databases must provide
6//! - Simple: Avoids type-system complexity and focuses on core functionality
7//!
8//! Rather than implementing business logic in each database backend, this API
9//! provides low-level primitives that higher-level code can build upon.
10//! This eliminates code duplication and makes adding new database backends trivial.
11//!
12//! The API differentiates between three types of database access:
13//!
14//! - Read views ([`StorageReadView`]): read-only views of the database,
15//! with no atomicity guarantees between operations.
16//! - Write batches ([`StorageWriteBatch`]): write batch functionality, with
17//! atomicity guarantees at commit time.
18//! - Locked views ([`StorageLockedView`]): read-only views of a point in time (snapshots), right now it's
19//! only used during snap-sync.
20
21use crate::error::StoreError;
22use std::{fmt::Debug, path::Path, sync::Arc};
23
24pub mod tables;
25
26/// Type alias for the result of a prefix iterator.
27pub type PrefixResult = Result<(Box<[u8]>, Box<[u8]>), StoreError>;
28
29/// This trait provides a minimal set of operations required from a database backend.
30/// Implementations should focus on providing efficient access to the underlying storage
31/// without implementing business logic.
32pub trait StorageBackend: Debug + Send + Sync {
33 /// Removes all data from the specified table.
34 fn clear_table(&self, table: &'static str) -> Result<(), StoreError>;
35
36 /// Opens a new read view.
37 fn begin_read(&self) -> Result<Arc<dyn StorageReadView>, StoreError>;
38
39 /// Creates a new write batch.
40 fn begin_write(&self) -> Result<Box<dyn StorageWriteBatch + 'static>, StoreError>;
41
42 /// Creates a locked snapshot for a specific table.
43 ///
44 /// This provides a persistent read-only view of a single table, optimized
45 /// for batch read operations. The snapshot remains valid until dropped.
46 fn begin_locked(
47 &self,
48 table_name: &'static str,
49 ) -> Result<Box<dyn StorageLockedView + 'static>, StoreError>;
50
51 // TODO: remove this and provide historic data via diff-layers
52 /// Creates a checkpoint of the current database state at the specified path.
53 fn create_checkpoint(&self, path: &Path) -> Result<(), StoreError>;
54}
55
56/// Read-only transaction interface.
57/// Provides methods to read data from the database
58pub trait StorageReadView: Send + Sync {
59 /// Retrieves a value by key from the specified table.
60 fn get(&self, table: &'static str, key: &[u8]) -> Result<Option<Vec<u8>>, StoreError>;
61
62 /// Returns an iterator over all key-value pairs with the given prefix.
63 fn prefix_iterator(
64 &self,
65 table: &'static str,
66 prefix: &[u8],
67 ) -> Result<Box<dyn Iterator<Item = PrefixResult> + '_>, StoreError>;
68}
69
70/// Write transaction interface.
71///
72/// Note that this does not provide read access, since we don't currently use that functionality.
73///
74/// Changes are not persisted until [`commit()`](StorageWriteBatch::commit) is called.
75pub trait StorageWriteBatch: Send {
76 /// Stores a key-value pair in the specified table.
77 fn put(&mut self, table: &'static str, key: &[u8], value: &[u8]) -> Result<(), StoreError> {
78 self.put_batch(table, vec![(key.to_vec(), value.to_vec())])
79 }
80
81 /// Stores multiple key-value pairs in the specified table within the transaction.
82 fn put_batch(
83 &mut self,
84 table: &'static str,
85 batch: Vec<(Vec<u8>, Vec<u8>)>,
86 ) -> Result<(), StoreError>;
87
88 /// Removes a key-value pair from the specified table.
89 fn delete(&mut self, table: &'static str, key: &[u8]) -> Result<(), StoreError>;
90
91 /// Appends a merge operand for the given key in the specified table.
92 ///
93 /// The actual combine step is deferred — backends with a registered merge
94 /// operator (RocksDB) apply it at read or compaction time; backends without
95 /// (InMemory) dispatch by table and apply inline.
96 ///
97 /// Currently used for `TRANSACTION_LOCATIONS`. Calling on a table without
98 /// a registered merge function is an error.
99 fn merge(&mut self, table: &'static str, key: &[u8], operand: &[u8]) -> Result<(), StoreError>;
100
101 /// Commits all changes made in this transaction.
102 fn commit(&mut self) -> Result<(), StoreError>;
103}
104
105/// Locked snapshot interface for batch read operations.
106/// Provides read-only access to a specific table with a persistent snapshot.
107/// This is optimized for scenarios where many reads are performed on the same
108/// table, such as trie traversal operations.
109/// This is currently only used in snapsync stage.
110// TODO: Check if we can remove this trait and use [`StorageReadView`] instead.
111pub trait StorageLockedView: Send + Sync {
112 /// Retrieves a value by key from the locked table.
113 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, StoreError>;
114}