Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 2 additions & 3 deletions crates/node-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ repository.workspace = true

[dependencies]
signet-blobber.workspace = true
signet-cold.workspace = true
signet-cold-mdbx.workspace = true
signet-evm.workspace = true
signet-hot-mdbx.workspace = true
signet-storage.workspace = true
signet-types.workspace = true

Expand All @@ -37,3 +34,5 @@ tempfile = { workspace = true, optional = true }

[features]
test_utils = ["dep:tempfile"]
postgres = ["signet-storage/postgres"]
sqlite = ["signet-storage/sqlite"]
81 changes: 63 additions & 18 deletions crates/node-config/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
use init4_bin_base::utils::from_env::FromEnv;
use signet_cold::ColdStorageTask;
use signet_cold_mdbx::MdbxColdBackend;
use signet_hot_mdbx::{DatabaseArguments, DatabaseEnv};
use signet_storage::UnifiedStorage;
#[cfg(any(feature = "postgres", feature = "sqlite"))]
use signet_storage::SqlConnector;
use signet_storage::{DatabaseEnv, MdbxConnector, UnifiedStorage, builder::StorageBuilder};
use std::borrow::Cow;
use tokio_util::sync::CancellationToken;

/// Configuration for signet unified storage.
///
/// Reads hot and cold MDBX paths from environment variables.
/// Reads hot and cold storage configuration from environment variables.
///
/// # Environment Variables
///
/// - `SIGNET_HOT_PATH` – Path to the hot MDBX database.
/// - `SIGNET_COLD_PATH` – Path to the cold MDBX database.
/// - `SIGNET_COLD_PATH` – Path to the cold MDBX database (mutually exclusive
/// with SQL URL).
/// - `SIGNET_COLD_SQL_URL` – SQL connection URL for cold storage (requires
/// `postgres` or `sqlite` feature).
///
/// Exactly one of `SIGNET_COLD_PATH` or `SIGNET_COLD_SQL_URL` must be set.
///
/// # Example
///
/// ```rust,no_run
/// # use signet_node_config::StorageConfig;
/// # use tokio_util::sync::CancellationToken;
/// # fn example(cfg: &StorageConfig) -> eyre::Result<()> {
/// # async fn example(cfg: &StorageConfig) -> eyre::Result<()> {
/// let cancel = CancellationToken::new();
/// let storage = cfg.build_storage(cancel)?;
/// let storage = cfg.build_storage(cancel).await?;
/// # Ok(())
/// # }
/// ```
Expand All @@ -35,12 +39,22 @@ pub struct StorageConfig {
/// Path to the cold MDBX database.
#[from_env(var = "SIGNET_COLD_PATH", desc = "Path to cold MDBX storage", infallible)]
cold_path: Cow<'static, str>,

/// SQL connection URL for cold storage (requires `postgres` or `sqlite`
/// feature).
#[from_env(
var = "SIGNET_COLD_SQL_URL",
desc = "SQL connection URL for cold storage",
infallible
)]
#[serde(default)]
cold_sql_url: Cow<'static, str>,
}

impl StorageConfig {
/// Create a new storage configuration.
/// Create a new storage configuration with MDBX cold backend.
pub const fn new(hot_path: Cow<'static, str>, cold_path: Cow<'static, str>) -> Self {
Self { hot_path, cold_path }
Self { hot_path, cold_path, cold_sql_url: Cow::Borrowed("") }
}

/// Get the hot storage path.
Expand All @@ -53,18 +67,49 @@ impl StorageConfig {
&self.cold_path
}

/// Get the cold SQL connection URL.
pub fn cold_sql_url(&self) -> &str {
&self.cold_sql_url
}

/// Build unified storage from this configuration.
///
/// Opens an MDBX read-write environment for both hot and cold storage,
/// spawns the cold storage background task, and returns a
/// [`UnifiedStorage`] ready for use.
pub fn build_storage(
/// Creates connectors from the configured paths, spawns the cold storage
/// background task, and returns a [`UnifiedStorage`] ready for use.
///
/// Exactly one of `cold_path` or `cold_sql_url` must be non-empty.
pub async fn build_storage(
&self,
cancel: CancellationToken,
) -> eyre::Result<UnifiedStorage<DatabaseEnv>> {
let hot = DatabaseArguments::new().open_rw(self.hot_path.as_ref().as_ref())?;
let cold_backend = MdbxColdBackend::open_rw(self.cold_path.as_ref().as_ref())?;
let cold_handle = ColdStorageTask::spawn(cold_backend, cancel);
Ok(UnifiedStorage::new(hot, cold_handle))
let hot = MdbxConnector::new(self.hot_path.as_ref());
let has_mdbx = !self.cold_path.is_empty();
let has_sql = !self.cold_sql_url.is_empty();

match (has_mdbx, has_sql) {
(true, false) => Ok(StorageBuilder::new()
.hot(hot)
.cold(MdbxConnector::new(self.cold_path.as_ref()))
.cancel_token(cancel)
.build()
.await?),
#[cfg(any(feature = "postgres", feature = "sqlite"))]
(false, true) => Ok(StorageBuilder::new()
.hot(hot)
.cold(SqlConnector::new(self.cold_sql_url.as_ref()))
.cancel_token(cancel)
.build()
.await?),
#[cfg(not(any(feature = "postgres", feature = "sqlite")))]
(false, true) => {
eyre::bail!("SIGNET_COLD_SQL_URL requires the 'postgres' or 'sqlite' feature")
}
(true, true) => eyre::bail!(
"both SIGNET_COLD_PATH and SIGNET_COLD_SQL_URL are set; specify exactly one"
),
(false, false) => eyre::bail!(
"neither SIGNET_COLD_PATH nor SIGNET_COLD_SQL_URL is set; specify exactly one"
),
}
}
}