Skip to content

Commit a71ba60

Browse files
prestwichclaude
andauthored
refactor(cold): remove metadata table and consolidate SQL queries (#22)
The metadata table only tracked latest_block, which is trivially derivable from the headers table. Remove it from all three backends (in-memory, MDBX, SQL) and derive get_latest_block from headers directly. Additionally consolidate SQL queries in the cold-sql backend: - get_transaction: use JOINs to fetch tx + block_hash in one query - get_receipt: use JOINs to fetch receipt + block_hash in one query - get_receipts_in_block: fix N+1 by batch-fetching all logs for a block instead of per-receipt - Remove 5 dead helper methods made redundant by the consolidation Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cad3793 commit a71ba60

File tree

7 files changed

+162
-397
lines changed

7 files changed

+162
-397
lines changed

crates/cold-mdbx/src/backend.rs

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
//! database environment from `signet-hot-mdbx`.
66
77
use crate::{
8-
ColdBlockHashIndex, ColdHeaders, ColdMetadata, ColdReceipts, ColdSignetEvents,
9-
ColdTransactions, ColdTxHashIndex, ColdZenithHeaders, MdbxColdError, MetadataKey,
8+
ColdBlockHashIndex, ColdHeaders, ColdReceipts, ColdSignetEvents, ColdTransactions,
9+
ColdTxHashIndex, ColdZenithHeaders, MdbxColdError,
1010
};
1111
use alloy::{consensus::Header, primitives::BlockNumber};
1212
use signet_cold::{
@@ -92,12 +92,6 @@ impl MdbxColdBackend {
9292
ColdTxHashIndex::FIXED_VAL_SIZE,
9393
ColdTxHashIndex::INT_KEY,
9494
),
95-
(
96-
ColdMetadata::NAME,
97-
ColdMetadata::DUAL_KEY_SIZE,
98-
ColdMetadata::FIXED_VAL_SIZE,
99-
ColdMetadata::INT_KEY,
100-
),
10195
(
10296
ColdTransactions::NAME,
10397
ColdTransactions::DUAL_KEY_SIZE,
@@ -124,11 +118,6 @@ impl MdbxColdBackend {
124118
Ok(())
125119
}
126120

127-
fn get_metadata(&self, key: MetadataKey) -> Result<Option<BlockNumber>, MdbxColdError> {
128-
let tx = self.env.tx()?;
129-
Ok(TableTraverse::<ColdMetadata, _>::exact(&mut tx.new_cursor::<ColdMetadata>()?, &key)?)
130-
}
131-
132121
fn get_header_inner(&self, spec: HeaderSpecifier) -> Result<Option<Header>, MdbxColdError> {
133122
let tx = self.env.tx()?;
134123
let block_num = match spec {
@@ -359,16 +348,6 @@ impl MdbxColdBackend {
359348
tx.queue_put::<ColdZenithHeaders>(&block, zh)?;
360349
}
361350

362-
// Update block bounds
363-
let current_latest: Option<BlockNumber> = TableTraverse::<ColdMetadata, _>::exact(
364-
&mut tx.new_cursor::<ColdMetadata>()?,
365-
&MetadataKey::LatestBlock,
366-
)?;
367-
tx.queue_put::<ColdMetadata>(
368-
&MetadataKey::LatestBlock,
369-
&current_latest.map_or(block, |prev| prev.max(block)),
370-
)?;
371-
372351
tx.raw_commit()?;
373352
Ok(())
374353
}
@@ -420,22 +399,6 @@ impl MdbxColdBackend {
420399
tx.queue_delete::<ColdZenithHeaders>(block_num)?;
421400
}
422401

423-
// Update latest block
424-
{
425-
let mut cursor = tx.new_cursor::<ColdHeaders>()?;
426-
match KvTraverse::<_>::last(&mut cursor)? {
427-
Some((key, _)) => {
428-
tx.queue_put::<ColdMetadata>(
429-
&MetadataKey::LatestBlock,
430-
&BlockNumber::decode_key(&key)?,
431-
)?;
432-
}
433-
None => {
434-
tx.queue_delete::<ColdMetadata>(&MetadataKey::LatestBlock)?;
435-
}
436-
}
437-
}
438-
439402
tx.raw_commit()?;
440403
Ok(())
441404
}
@@ -575,7 +538,14 @@ impl ColdStorage for MdbxColdBackend {
575538
}
576539

577540
async fn get_latest_block(&self) -> ColdResult<Option<BlockNumber>> {
578-
Ok(self.get_metadata(MetadataKey::LatestBlock)?)
541+
let tx = self.env.tx().map_err(MdbxColdError::from)?;
542+
let mut cursor = tx.new_cursor::<ColdHeaders>().map_err(MdbxColdError::from)?;
543+
let latest = KvTraverse::<_>::last(&mut cursor)
544+
.map_err(MdbxColdError::from)?
545+
.map(|(key, _)| BlockNumber::decode_key(&key))
546+
.transpose()
547+
.map_err(MdbxColdError::from)?;
548+
Ok(latest)
579549
}
580550

581551
async fn get_receipt_with_context(

crates/cold-mdbx/src/lib.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
//! - [`ColdBlockHashIndex`]: Maps block hash to block number.
1717
//! - [`ColdTxHashIndex`]: Maps transaction hash to (block number, tx index).
1818
//!
19-
//! ## Metadata Tables
20-
//!
21-
//! - [`ColdMetadata`]: Storage metadata (latest block, finalized, safe, earliest).
22-
//!
2319
//! # Feature Flags
2420
//!
2521
//! - **`test-utils`**: Propagates `signet-cold/test-utils` for conformance
@@ -42,8 +38,8 @@ pub use error::MdbxColdError;
4238

4339
mod tables;
4440
pub use tables::{
45-
ColdBlockHashIndex, ColdHeaders, ColdMetadata, ColdReceipts, ColdSignetEvents,
46-
ColdTransactions, ColdTxHashIndex, ColdZenithHeaders, MetadataKey,
41+
ColdBlockHashIndex, ColdHeaders, ColdReceipts, ColdSignetEvents, ColdTransactions,
42+
ColdTxHashIndex, ColdZenithHeaders,
4743
};
4844

4945
mod backend;

crates/cold-mdbx/src/tables.rs

Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,10 @@
44
//! the [`Table`], [`SingleKey`], and [`DualKey`] traits.
55
66
use alloy::{consensus::Header, primitives::B256, primitives::BlockNumber};
7-
use signet_hot::ser::{DeserError, KeySer, MAX_KEY_SIZE};
7+
use signet_hot::ser::KeySer;
88
use signet_hot::tables::{DualKey, SingleKey, Table};
99
use signet_storage_types::{DbSignetEvent, DbZenithHeader, Receipt, TransactionSigned, TxLocation};
1010

11-
// ============================================================================
12-
// Metadata Key Enum
13-
// ============================================================================
14-
15-
/// Keys for the cold storage metadata table.
16-
///
17-
/// These are used to store semantic block references like "latest" or
18-
/// "finalized" that need to be resolved to actual block numbers.
19-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20-
#[repr(u8)]
21-
pub enum MetadataKey {
22-
/// The latest (most recent) block number stored.
23-
LatestBlock = 0,
24-
}
25-
26-
impl TryFrom<u8> for MetadataKey {
27-
type Error = DeserError;
28-
29-
fn try_from(value: u8) -> Result<Self, Self::Error> {
30-
match value {
31-
0 => Ok(Self::LatestBlock),
32-
_ => Err(DeserError::String(format!("Invalid MetadataKey value: {}", value))),
33-
}
34-
}
35-
}
36-
37-
impl KeySer for MetadataKey {
38-
const SIZE: usize = 1;
39-
40-
fn encode_key<'a: 'c, 'b: 'c, 'c>(&'a self, buf: &'b mut [u8; MAX_KEY_SIZE]) -> &'c [u8] {
41-
buf[0] = *self as u8;
42-
&buf[..1]
43-
}
44-
45-
fn decode_key(data: &[u8]) -> Result<Self, DeserError> {
46-
if data.is_empty() {
47-
return Err(DeserError::InsufficientData { needed: 1, available: 0 });
48-
}
49-
MetadataKey::try_from(data[0])
50-
}
51-
}
52-
5311
// ============================================================================
5412
// Primary Data Tables
5513
// ============================================================================
@@ -189,24 +147,6 @@ impl Table for ColdTxHashIndex {
189147

190148
impl SingleKey for ColdTxHashIndex {}
191149

192-
// ============================================================================
193-
// Metadata Table
194-
// ============================================================================
195-
196-
/// Cold storage metadata.
197-
///
198-
/// Keys: `LatestBlock(0)`
199-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
200-
pub struct ColdMetadata;
201-
202-
impl Table for ColdMetadata {
203-
const NAME: &'static str = "ColdMetadata";
204-
type Key = MetadataKey;
205-
type Value = BlockNumber;
206-
}
207-
208-
impl SingleKey for ColdMetadata {}
209-
210150
#[cfg(test)]
211151
mod tests {
212152
use super::*;
@@ -220,7 +160,6 @@ mod tests {
220160
assert_eq!(ColdZenithHeaders::NAME, "ColdZenithHeaders");
221161
assert_eq!(ColdBlockHashIndex::NAME, "ColdBlockHashIndex");
222162
assert_eq!(ColdTxHashIndex::NAME, "ColdTxHashIndex");
223-
assert_eq!(ColdMetadata::NAME, "ColdMetadata");
224163
}
225164

226165
#[test]
@@ -232,7 +171,6 @@ mod tests {
232171
assert_single_key::<ColdZenithHeaders>();
233172
assert_single_key::<ColdBlockHashIndex>();
234173
assert_single_key::<ColdTxHashIndex>();
235-
assert_single_key::<ColdMetadata>();
236174
}
237175

238176
#[test]
@@ -257,7 +195,6 @@ mod tests {
257195
// Non-int_key tables should have INT_KEY = false
258196
const { assert!(!ColdBlockHashIndex::INT_KEY) };
259197
const { assert!(!ColdTxHashIndex::INT_KEY) };
260-
const { assert!(!ColdMetadata::INT_KEY) };
261198
}
262199

263200
#[test]
@@ -268,28 +205,8 @@ mod tests {
268205
// ColdBlockHashIndex has BlockNumber (u64) = 8 bytes
269206
assert_eq!(ColdBlockHashIndex::FIXED_VAL_SIZE, Some(8));
270207

271-
// ColdMetadata also has BlockNumber (u64) = 8 bytes
272-
assert_eq!(ColdMetadata::FIXED_VAL_SIZE, Some(8));
273-
274208
// Variable-size value tables should not have fixed value size
275209
assert_eq!(ColdHeaders::FIXED_VAL_SIZE, None);
276210
assert_eq!(ColdZenithHeaders::FIXED_VAL_SIZE, None);
277211
}
278-
279-
#[test]
280-
fn test_metadata_key_try_from() {
281-
assert_eq!(MetadataKey::try_from(0).unwrap(), MetadataKey::LatestBlock);
282-
assert!(MetadataKey::try_from(1).is_err());
283-
assert!(MetadataKey::try_from(255).is_err());
284-
}
285-
286-
#[test]
287-
fn test_metadata_key_ser_roundtrip() {
288-
let mut buf = [0u8; MAX_KEY_SIZE];
289-
let encoded = MetadataKey::LatestBlock.encode_key(&mut buf);
290-
assert_eq!(encoded.len(), 1);
291-
292-
let decoded = MetadataKey::decode_key(encoded).unwrap();
293-
assert_eq!(MetadataKey::LatestBlock, decoded);
294-
}
295212
}

crates/cold-sql/migrations/001_initial.sql

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,3 @@ CREATE TABLE IF NOT EXISTS zenith_headers (
108108
reward_address BLOB NOT NULL,
109109
block_data_hash BLOB NOT NULL
110110
);
111-
112-
CREATE TABLE IF NOT EXISTS metadata (
113-
key TEXT PRIMARY KEY NOT NULL,
114-
block_number INTEGER NOT NULL
115-
);

crates/cold-sql/migrations/001_initial_pg.sql

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,3 @@ CREATE TABLE IF NOT EXISTS zenith_headers (
106106
reward_address BYTEA NOT NULL,
107107
block_data_hash BYTEA NOT NULL
108108
);
109-
110-
CREATE TABLE IF NOT EXISTS metadata (
111-
key TEXT PRIMARY KEY NOT NULL,
112-
block_number BIGINT NOT NULL
113-
);

0 commit comments

Comments
 (0)