mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: introduce custom exex wal errors (#11789)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
This commit is contained in:
@ -47,6 +47,7 @@ itertools = { workspace = true, features = ["use_std"] }
|
||||
metrics.workspace = true
|
||||
parking_lot.workspace = true
|
||||
rmp-serde = "1.3"
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@ -657,6 +657,7 @@ impl<N: NodePrimitives> Clone for ExExManagerHandle<N> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::wal::WalResult;
|
||||
use alloy_primitives::B256;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use rand::Rng;
|
||||
@ -1356,7 +1357,7 @@ mod tests {
|
||||
);
|
||||
// WAL shouldn't contain the genesis notification, because it's finalized
|
||||
assert_eq!(
|
||||
exex_manager.wal.iter_notifications()?.collect::<eyre::Result<Vec<_>>>()?,
|
||||
exex_manager.wal.iter_notifications()?.collect::<WalResult<Vec<_>>>()?,
|
||||
[notification.clone()]
|
||||
);
|
||||
|
||||
@ -1364,7 +1365,7 @@ mod tests {
|
||||
assert!(exex_manager.as_mut().poll(&mut cx).is_pending());
|
||||
// WAL isn't finalized because the ExEx didn't emit the `FinishedHeight` event
|
||||
assert_eq!(
|
||||
exex_manager.wal.iter_notifications()?.collect::<eyre::Result<Vec<_>>>()?,
|
||||
exex_manager.wal.iter_notifications()?.collect::<WalResult<Vec<_>>>()?,
|
||||
[notification.clone()]
|
||||
);
|
||||
|
||||
@ -1378,7 +1379,7 @@ mod tests {
|
||||
// WAL isn't finalized because the ExEx emitted a `FinishedHeight` event with a
|
||||
// non-canonical block
|
||||
assert_eq!(
|
||||
exex_manager.wal.iter_notifications()?.collect::<eyre::Result<Vec<_>>>()?,
|
||||
exex_manager.wal.iter_notifications()?.collect::<WalResult<Vec<_>>>()?,
|
||||
[notification]
|
||||
);
|
||||
|
||||
|
||||
29
crates/exex/exex/src/wal/error.rs
Normal file
29
crates/exex/exex/src/wal/error.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! Wal Errors
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Wal Result type.
|
||||
pub type WalResult<T> = Result<T, WalError>;
|
||||
|
||||
/// Wal Error types
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WalError {
|
||||
/// Filesystem error at the path
|
||||
#[error(transparent)]
|
||||
FsPathError(#[from] reth_fs_util::FsPathError),
|
||||
/// Directory entry reading error
|
||||
#[error("failed to get {0} directory entry: {1}")]
|
||||
DirEntry(PathBuf, std::io::Error),
|
||||
/// Error when reading file metadata
|
||||
#[error("failed to get metadata for file {0}: {1}")]
|
||||
FileMetadata(u32, std::io::Error),
|
||||
/// Parse error
|
||||
#[error("failed to parse file name: {0}")]
|
||||
Parse(String),
|
||||
/// Notification not found error
|
||||
#[error("notification {0} not found")]
|
||||
FileNotFound(u32),
|
||||
/// Decode error
|
||||
#[error("failed to decode notification {0} from {1}: {2}")]
|
||||
Decode(u32, PathBuf, rmp_serde::decode::Error),
|
||||
}
|
||||
@ -8,6 +8,8 @@ use reth_primitives::EthPrimitives;
|
||||
pub use storage::Storage;
|
||||
mod metrics;
|
||||
use metrics::Metrics;
|
||||
mod error;
|
||||
pub use error::{WalError, WalResult};
|
||||
|
||||
use std::{
|
||||
path::Path,
|
||||
@ -43,7 +45,7 @@ where
|
||||
N: NodePrimitives,
|
||||
{
|
||||
/// Creates a new instance of [`Wal`].
|
||||
pub fn new(directory: impl AsRef<Path>) -> eyre::Result<Self> {
|
||||
pub fn new(directory: impl AsRef<Path>) -> WalResult<Self> {
|
||||
Ok(Self { inner: Arc::new(WalInner::new(directory)?) })
|
||||
}
|
||||
|
||||
@ -53,7 +55,7 @@ where
|
||||
}
|
||||
|
||||
/// Commits the notification to WAL.
|
||||
pub fn commit(&self, notification: &ExExNotification<N>) -> eyre::Result<()> {
|
||||
pub fn commit(&self, notification: &ExExNotification<N>) -> WalResult<()> {
|
||||
self.inner.commit(notification)
|
||||
}
|
||||
|
||||
@ -61,14 +63,14 @@ where
|
||||
///
|
||||
/// The caller should check that all ExExes are on the canonical chain and will not need any
|
||||
/// blocks from the WAL below the provided block, inclusive.
|
||||
pub fn finalize(&self, to_block: BlockNumHash) -> eyre::Result<()> {
|
||||
pub fn finalize(&self, to_block: BlockNumHash) -> WalResult<()> {
|
||||
self.inner.finalize(to_block)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all notifications in the WAL.
|
||||
pub fn iter_notifications(
|
||||
&self,
|
||||
) -> eyre::Result<Box<dyn Iterator<Item = eyre::Result<ExExNotification<N>>> + '_>> {
|
||||
) -> WalResult<Box<dyn Iterator<Item = WalResult<ExExNotification<N>>> + '_>> {
|
||||
self.inner.iter_notifications()
|
||||
}
|
||||
|
||||
@ -93,7 +95,7 @@ impl<N> WalInner<N>
|
||||
where
|
||||
N: NodePrimitives,
|
||||
{
|
||||
fn new(directory: impl AsRef<Path>) -> eyre::Result<Self> {
|
||||
fn new(directory: impl AsRef<Path>) -> WalResult<Self> {
|
||||
let mut wal = Self {
|
||||
next_file_id: AtomicU32::new(0),
|
||||
storage: Storage::new(directory)?,
|
||||
@ -110,7 +112,7 @@ where
|
||||
|
||||
/// Fills the block cache with the notifications from the storage.
|
||||
#[instrument(skip(self))]
|
||||
fn fill_block_cache(&mut self) -> eyre::Result<()> {
|
||||
fn fill_block_cache(&mut self) -> WalResult<()> {
|
||||
let Some(files_range) = self.storage.files_range()? else { return Ok(()) };
|
||||
self.next_file_id.store(files_range.end() + 1, Ordering::Relaxed);
|
||||
|
||||
@ -145,7 +147,7 @@ where
|
||||
reverted_block_range = ?notification.reverted_chain().as_ref().map(|chain| chain.range()),
|
||||
committed_block_range = ?notification.committed_chain().as_ref().map(|chain| chain.range())
|
||||
))]
|
||||
fn commit(&self, notification: &ExExNotification<N>) -> eyre::Result<()> {
|
||||
fn commit(&self, notification: &ExExNotification<N>) -> WalResult<()> {
|
||||
let mut block_cache = self.block_cache.write();
|
||||
|
||||
let file_id = self.next_file_id.fetch_add(1, Ordering::Relaxed);
|
||||
@ -160,7 +162,7 @@ where
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
fn finalize(&self, to_block: BlockNumHash) -> eyre::Result<()> {
|
||||
fn finalize(&self, to_block: BlockNumHash) -> WalResult<()> {
|
||||
let mut block_cache = self.block_cache.write();
|
||||
let file_ids = block_cache.remove_before(to_block.number);
|
||||
|
||||
@ -195,7 +197,7 @@ where
|
||||
/// Returns an iterator over all notifications in the WAL.
|
||||
fn iter_notifications(
|
||||
&self,
|
||||
) -> eyre::Result<Box<dyn Iterator<Item = eyre::Result<ExExNotification<N>>> + '_>> {
|
||||
) -> WalResult<Box<dyn Iterator<Item = WalResult<ExExNotification<N>>> + '_>> {
|
||||
let Some(range) = self.storage.files_range()? else {
|
||||
return Ok(Box::new(std::iter::empty()))
|
||||
};
|
||||
@ -218,7 +220,7 @@ where
|
||||
pub fn get_committed_notification_by_block_hash(
|
||||
&self,
|
||||
block_hash: &B256,
|
||||
) -> eyre::Result<Option<ExExNotification<N>>> {
|
||||
) -> WalResult<Option<ExExNotification<N>>> {
|
||||
let Some(file_id) = self.wal.block_cache().get_file_id_by_committed_block_hash(block_hash)
|
||||
else {
|
||||
return Ok(None)
|
||||
@ -233,7 +235,7 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::wal::{cache::CachedBlock, Wal};
|
||||
use crate::wal::{cache::CachedBlock, error::WalResult, Wal};
|
||||
use alloy_primitives::B256;
|
||||
use itertools::Itertools;
|
||||
use reth_exex_types::ExExNotification;
|
||||
@ -243,7 +245,7 @@ mod tests {
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn read_notifications(wal: &Wal) -> eyre::Result<Vec<ExExNotification>> {
|
||||
fn read_notifications(wal: &Wal) -> WalResult<Vec<ExExNotification>> {
|
||||
wal.inner.storage.files_range()?.map_or(Ok(Vec::new()), |range| {
|
||||
wal.inner
|
||||
.storage
|
||||
|
||||
@ -4,7 +4,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use eyre::OptionExt;
|
||||
use crate::wal::{WalError, WalResult};
|
||||
use reth_exex_types::ExExNotification;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_primitives::EthPrimitives;
|
||||
@ -30,7 +30,7 @@ where
|
||||
{
|
||||
/// Creates a new instance of [`Storage`] backed by the file at the given path and creates
|
||||
/// it doesn't exist.
|
||||
pub(super) fn new(path: impl AsRef<Path>) -> eyre::Result<Self> {
|
||||
pub(super) fn new(path: impl AsRef<Path>) -> WalResult<Self> {
|
||||
reth_fs_util::create_dir_all(&path)?;
|
||||
|
||||
Ok(Self { path: path.as_ref().to_path_buf(), _pd: std::marker::PhantomData })
|
||||
@ -40,11 +40,11 @@ where
|
||||
self.path.join(format!("{id}.{FILE_EXTENSION}"))
|
||||
}
|
||||
|
||||
fn parse_filename(filename: &str) -> eyre::Result<u32> {
|
||||
fn parse_filename(filename: &str) -> WalResult<u32> {
|
||||
filename
|
||||
.strip_suffix(".wal")
|
||||
.and_then(|s| s.parse().ok())
|
||||
.ok_or_eyre(format!("failed to parse file name: {filename}"))
|
||||
.ok_or_else(|| WalError::Parse(filename.to_string()))
|
||||
}
|
||||
|
||||
/// Removes notification for the given file ID from the storage.
|
||||
@ -72,12 +72,12 @@ where
|
||||
/// Returns the range of file IDs in the storage.
|
||||
///
|
||||
/// If there are no files in the storage, returns `None`.
|
||||
pub(super) fn files_range(&self) -> eyre::Result<Option<RangeInclusive<u32>>> {
|
||||
pub(super) fn files_range(&self) -> WalResult<Option<RangeInclusive<u32>>> {
|
||||
let mut min_id = None;
|
||||
let mut max_id = None;
|
||||
|
||||
for entry in reth_fs_util::read_dir(&self.path)? {
|
||||
let entry = entry?;
|
||||
let entry = entry.map_err(|err| WalError::DirEntry(self.path.clone(), err))?;
|
||||
|
||||
if entry.path().extension() == Some(FILE_EXTENSION.as_ref()) {
|
||||
let file_name = entry.file_name();
|
||||
@ -99,7 +99,7 @@ where
|
||||
pub(super) fn remove_notifications(
|
||||
&self,
|
||||
file_ids: impl IntoIterator<Item = u32>,
|
||||
) -> eyre::Result<(usize, u64)> {
|
||||
) -> WalResult<(usize, u64)> {
|
||||
let mut deleted_total = 0;
|
||||
let mut deleted_size = 0;
|
||||
|
||||
@ -116,10 +116,10 @@ where
|
||||
pub(super) fn iter_notifications(
|
||||
&self,
|
||||
range: RangeInclusive<u32>,
|
||||
) -> impl Iterator<Item = eyre::Result<(u32, u64, ExExNotification<N>)>> + '_ {
|
||||
) -> impl Iterator<Item = WalResult<(u32, u64, ExExNotification<N>)>> + '_ {
|
||||
range.map(move |id| {
|
||||
let (notification, size) =
|
||||
self.read_notification(id)?.ok_or_eyre(format!("notification {id} not found"))?;
|
||||
self.read_notification(id)?.ok_or(WalError::FileNotFound(id))?;
|
||||
|
||||
Ok((id, size, notification))
|
||||
})
|
||||
@ -130,7 +130,7 @@ where
|
||||
pub(super) fn read_notification(
|
||||
&self,
|
||||
file_id: u32,
|
||||
) -> eyre::Result<Option<(ExExNotification<N>, u64)>> {
|
||||
) -> WalResult<Option<(ExExNotification<N>, u64)>> {
|
||||
let file_path = self.file_path(file_id);
|
||||
debug!(target: "exex::wal::storage", ?file_path, "Reading notification from WAL");
|
||||
|
||||
@ -139,13 +139,12 @@ where
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
||||
Err(err) => return Err(reth_fs_util::FsPathError::open(err, &file_path).into()),
|
||||
};
|
||||
let size = file.metadata()?.len();
|
||||
let size = file.metadata().map_err(|err| WalError::FileMetadata(file_id, err))?.len();
|
||||
|
||||
// Deserialize using the bincode- and msgpack-compatible serde wrapper
|
||||
let notification: reth_exex_types::serde_bincode_compat::ExExNotification<'_, N> =
|
||||
rmp_serde::decode::from_read(&mut file).map_err(|err| {
|
||||
eyre::eyre!("failed to decode notification from {file_path:?}: {err:?}")
|
||||
})?;
|
||||
rmp_serde::decode::from_read(&mut file)
|
||||
.map_err(|err| WalError::Decode(file_id, file_path, err))?;
|
||||
|
||||
Ok(Some((notification.into(), size)))
|
||||
}
|
||||
@ -160,7 +159,7 @@ where
|
||||
&self,
|
||||
file_id: u32,
|
||||
notification: &ExExNotification<N>,
|
||||
) -> eyre::Result<u64> {
|
||||
) -> WalResult<u64> {
|
||||
let file_path = self.file_path(file_id);
|
||||
debug!(target: "exex::wal::storage", ?file_path, "Writing notification to WAL");
|
||||
|
||||
@ -172,7 +171,7 @@ where
|
||||
rmp_serde::encode::write(file, ¬ification)
|
||||
})?;
|
||||
|
||||
Ok(file_path.metadata()?.len())
|
||||
Ok(file_path.metadata().map_err(|err| WalError::FileMetadata(file_id, err))?.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user