From 89b95f6bae15c45bcb89668a0e7c63e549fa9976 Mon Sep 17 00:00:00 2001 From: Swanny Date: Thu, 26 Mar 2026 18:32:22 -0400 Subject: [PATCH 1/3] fix(node): create hot storage tables before first read in prebuild On a fresh MDBX database, named tables do not exist until explicitly created in a write transaction. The prebuild() method was attempting to read from the "Headers" table via a read-only transaction, which cannot create tables, causing MDBX_NOTFOUND ("no matching key/data pair found") to propagate as a fatal error. This adds an initialization step that creates all standard hot storage tables in a write transaction before any reads. The create operation is idempotent, so it is safe on existing databases. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/node/src/builder.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/node/src/builder.rs b/crates/node/src/builder.rs index 3995070..68610f8 100644 --- a/crates/node/src/builder.rs +++ b/crates/node/src/builder.rs @@ -5,7 +5,11 @@ use eyre::OptionExt; use signet_blobber::CacheHandle; use signet_block_processor::AliasOracleFactory; use signet_cold::BlockData; -use signet_hot::db::{HotDbRead, UnsafeDbWrite}; +use signet_hot::{ + db::{HotDbRead, UnsafeDbWrite}, + model::HotKvWrite, + tables, +}; use signet_node_config::SignetNodeConfig; use signet_node_types::HostNotifier; use signet_rpc::{ServeConfig, StorageRpcConfig}; @@ -177,6 +181,23 @@ where self.notifier.as_ref().ok_or_eyre("Notifier must be set")?; let storage = self.storage.as_ref().ok_or_eyre("Storage must be set")?; + // Ensure all hot storage tables exist. On a fresh MDBX database, + // named tables must be created in a write transaction before + // read-only transactions can open them. + { + let writer = storage.hot().writer()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.queue_create::()?; + writer.raw_commit()?; + } + // Load genesis into hot storage if absent. let reader = storage.reader()?; let has_hot_genesis = reader.has_block(0)?; From 7aaca5935e7042d71e89766b5312c08333776dee Mon Sep 17 00:00:00 2001 From: Swanny Date: Thu, 26 Mar 2026 18:52:51 -0400 Subject: [PATCH 2/3] fix(node): handle cold storage backpressure gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During initial sync or rapid block processing, the cold storage channel can fill up faster than the PostgreSQL backend can drain it. The UnifiedStorage docs explicitly state that backpressure is transient and should be logged, not treated as fatal — hot storage is authoritative and cold will catch up. Change append_blocks error handling to log cold storage errors as warnings instead of crashing the node. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/node/src/node.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/node/src/node.rs b/crates/node/src/node.rs index ea045fc..b3d4146 100644 --- a/crates/node/src/node.rs +++ b/crates/node/src/node.rs @@ -11,7 +11,7 @@ use signet_rpc::{ ChainNotifier, NewBlockNotification, RemovedBlock, ReorgNotification, RpcServerGuard, ServeConfig, StorageRpcConfig, }; -use signet_storage::{DrainedBlock, HistoryRead, HotKv, HotKvRead, UnifiedStorage}; +use signet_storage::{DrainedBlock, HistoryRead, HotKv, HotKvRead, StorageError, UnifiedStorage}; use signet_types::{PairedHeights, constants::SignetSystemConstants}; use std::{fmt, sync::Arc}; use tokio::sync::watch; @@ -282,7 +282,15 @@ where ); let executed = processor.process_block(block_extracts).await?; self.notify_new_block(&executed); - self.storage.append_blocks(vec![executed])?; + // Cold backpressure is non-fatal: hot storage is authoritative + // and cold will catch up once the channel drains. + match self.storage.append_blocks(vec![executed]) { + Ok(()) => {} + Err(StorageError::Cold(e)) => { + tracing::warn!(%e, "cold storage backpressure, hot storage is ahead"); + } + Err(e) => return Err(e.into()), + } processed = true; } Ok(processed) From 5acbc9c38d1554125eb5a216a58ca37a539cf675 Mon Sep 17 00:00:00 2001 From: Swanny Date: Thu, 26 Mar 2026 19:00:37 -0400 Subject: [PATCH 3/3] fix(node): demote cold backpressure log to debug level During initial sync this fires on every block, flooding the logs. Demote to debug since it's expected transient behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/node/src/node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/node/src/node.rs b/crates/node/src/node.rs index b3d4146..ff8fc24 100644 --- a/crates/node/src/node.rs +++ b/crates/node/src/node.rs @@ -287,7 +287,7 @@ where match self.storage.append_blocks(vec![executed]) { Ok(()) => {} Err(StorageError::Cold(e)) => { - tracing::warn!(%e, "cold storage backpressure, hot storage is ahead"); + tracing::debug!(%e, "cold storage backpressure, hot storage is ahead"); } Err(e) => return Err(e.into()), }