Skip to main content

reth_storage_api/
metadata.rs

1//! Metadata provider trait for reading and writing node metadata.
2
3use alloc::vec::Vec;
4pub use reth_db_api::models::PipelineUnwind;
5use reth_db_api::models::{SnapAttempt, StorageSettings, SNAP_ATTEMPT_VERSION};
6use reth_storage_errors::provider::{ProviderError, ProviderResult};
7
8/// Metadata keys.
9pub mod keys {
10    /// Storage configuration settings for this node.
11    pub const STORAGE_SETTINGS: &str = "storage_settings";
12
13    /// A pipeline unwind that has not completed.
14    pub const PIPELINE_UNWIND: &str = "pipeline_unwind";
15
16    /// The snap synchronization attempt that owns downloaded state.
17    pub const SNAP_ATTEMPT: &str = "snap_attempt";
18}
19
20/// Client trait for reading node metadata from the database.
21#[auto_impl::auto_impl(&, Arc)]
22pub trait MetadataProvider: Send {
23    /// Get a metadata value by key
24    fn get_metadata(&self, key: &str) -> ProviderResult<Option<Vec<u8>>>;
25
26    /// Returns an interrupted pipeline unwind. Unreadable records must not be ignored because
27    /// stage checkpoints may describe different versions of the state.
28    fn pipeline_unwind(&self) -> ProviderResult<Option<PipelineUnwind>> {
29        self.get_metadata(keys::PIPELINE_UNWIND)?
30            .map(|bytes| serde_json::from_slice(&bytes).map_err(ProviderError::other))
31            .transpose()
32    }
33
34    /// Get storage settings for this node.
35    ///
36    /// If the stored metadata can't be deserialized (e.g. the format changed),
37    /// this returns `None` instead of an error so commands like `db clear` can
38    /// still operate without requiring a compatible metadata schema.
39    fn storage_settings(&self) -> ProviderResult<Option<StorageSettings>> {
40        Ok(self
41            .get_metadata(keys::STORAGE_SETTINGS)?
42            .and_then(|bytes| serde_json::from_slice(&bytes).ok()))
43    }
44
45    /// Returns the snap synchronization attempt that owns the downloaded state.
46    ///
47    /// Unlike [`Self::storage_settings`], an unreadable record is an error: its state is already
48    /// in the canonical tables, so reporting it absent would let the node adopt it.
49    fn snap_attempt(&self) -> ProviderResult<Option<SnapAttempt>> {
50        let Some(bytes) = self.get_metadata(keys::SNAP_ATTEMPT)? else { return Ok(None) };
51
52        // Read the version first, so a future build's record is reported as unsupported rather
53        // than as a decode failure.
54        let value: serde_json::Value =
55            serde_json::from_slice(&bytes).map_err(ProviderError::other)?;
56        let found = value.get("version").and_then(serde_json::Value::as_u64);
57        if found != Some(SNAP_ATTEMPT_VERSION as u64) {
58            return Err(ProviderError::UnsupportedSnapAttemptVersion {
59                found,
60                supported: SNAP_ATTEMPT_VERSION,
61            })
62        }
63
64        serde_json::from_slice(&bytes).map(Some).map_err(ProviderError::other)
65    }
66}
67
68/// Client trait for writing node metadata to the database.
69pub trait MetadataWriter: Send {
70    /// Write a metadata value
71    fn write_metadata(&self, key: &str, value: Vec<u8>) -> ProviderResult<()>;
72
73    /// Delete a metadata value.
74    fn delete_metadata(&self, _key: &str) -> ProviderResult<()> {
75        Err(ProviderError::UnsupportedProvider)
76    }
77
78    /// Records an unwind, or clears it after every selected stage has committed.
79    fn write_pipeline_unwind(&self, unwind: Option<&PipelineUnwind>) -> ProviderResult<()> {
80        match unwind {
81            Some(unwind) => self.write_metadata(
82                keys::PIPELINE_UNWIND,
83                serde_json::to_vec(unwind).map_err(ProviderError::other)?,
84            ),
85            None => self.delete_metadata(keys::PIPELINE_UNWIND),
86        }
87    }
88
89    /// Write storage settings for this node
90    ///
91    /// Be sure to update provider factory cache with
92    /// [`StorageSettingsCache::set_storage_settings_cache`].
93    fn write_storage_settings(&self, settings: StorageSettings) -> ProviderResult<()> {
94        self.write_metadata(
95            keys::STORAGE_SETTINGS,
96            serde_json::to_vec(&settings).map_err(ProviderError::other)?,
97        )
98    }
99
100    /// Writes the snap synchronization attempt that owns the downloaded state.
101    fn write_snap_attempt(&self, attempt: &SnapAttempt) -> ProviderResult<()> {
102        self.write_metadata(
103            keys::SNAP_ATTEMPT,
104            serde_json::to_vec(attempt).map_err(ProviderError::other)?,
105        )
106    }
107}
108
109/// Trait for caching storage settings on a provider factory.
110pub trait StorageSettingsCache: Send {
111    /// Gets the cached storage settings.
112    fn cached_storage_settings(&self) -> StorageSettings;
113
114    /// Sets the storage settings of this `ProviderFactory`.
115    ///
116    /// IMPORTANT: It does not save settings in storage, that should be done by
117    /// [`MetadataWriter::write_storage_settings`]
118    fn set_storage_settings_cache(&self, settings: StorageSettings);
119}
120
121/// Trait for accessing the database directory path.
122#[cfg(feature = "std")]
123pub trait StoragePath: Send {
124    /// Returns the path to the database directory (e.g. `<datadir>/db`).
125    fn storage_path(&self) -> std::path::PathBuf;
126}