Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9132801
add migration + trait
ss-es May 18, 2026
b0ed7c8
fix
ss-es May 18, 2026
e99ee1c
fix
ss-es May 18, 2026
dca3635
fix
ss-es May 18, 2026
dfbbc86
run migrations on node startup
ss-es May 18, 2026
89ede6d
fix
ss-es May 18, 2026
2c2934e
remove unused reward_merkle_trees
ss-es May 18, 2026
5c02df3
refactor?
ss-es May 18, 2026
63c1e6e
fix
ss-es May 19, 2026
038de43
fix
ss-es May 19, 2026
910f7fe
remove v3 tests
ss-es May 19, 2026
2bea604
cleanup
ss-es May 19, 2026
ac322b1
update
ss-es May 19, 2026
945f808
remove more dead tests
ss-es May 19, 2026
402ad04
cleanup gating
ss-es May 19, 2026
784cf19
clippy
ss-es May 20, 2026
4049af6
more cleanup
ss-es May 20, 2026
bbcd29b
reorganize
ss-es May 20, 2026
6d794d7
use unnest for batching
ss-es May 20, 2026
a8f43c8
page backfill
ss-es May 20, 2026
f3db054
simplify query fallback
ss-es May 21, 2026
27e5cad
cleanup
ss-es May 21, 2026
ef8dc5d
more simplification
ss-es May 21, 2026
a823f07
add tests
ss-es May 21, 2026
2cda1a3
fix?
ss-es May 21, 2026
9d6763c
add 50ms sleep between batches
ss-es May 22, 2026
5bfb3d7
make delay configurable
ss-es May 22, 2026
5ef0bf9
mitigate storage increase during migration
ss-es May 22, 2026
6a54800
add migration progress endpoint
ss-es May 22, 2026
424e78f
fix
ss-es May 22, 2026
e6020be
fix(persistence): translate hash_ids by value in merkle backfill (#4349)
sveitser May 24, 2026
8647739
fix migrations
ss-es May 25, 2026
9461a0f
update genesis (fix?)
ss-es May 26, 2026
68adf63
try fixing slow query?
ss-es May 26, 2026
f0d0519
fix attempt 2
ss-es May 26, 2026
2c1f93a
Revert "fix attempt 2"
ss-es May 27, 2026
dc85dab
Merge branch 'release-mainnet-1.2.0-rc' into release-test-hash-id-mig…
lukaszrzasik May 27, 2026
6df71ba
update to LATERAL instead of UNION
ss-es May 28, 2026
d4974cf
Merge branch 'release-mainnet-1.2.0-rc' into release-test-hash-id-mig…
ss-es Jun 1, 2026
63c74fe
add retries + sqlite pragmas for light client database
ss-es Jun 4, 2026
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
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ snafu = "0.8"
snow = { version = "0.10.0", features = ["ring-accelerated"] }
sqlx = { version = "=0.8.3", features = [
"bit-vec",
"chrono",
"postgres",
"runtime-tokio",
"tls-native-tls",
Expand Down
10 changes: 10 additions & 0 deletions crates/espresso/node/api/database.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
[route.get_migration_status]
PATH = ["migration-status"]
METHOD = "GET"
DOC = """
Get the status of all deferred background migrations.
Returns a list of migrations with their name, when they started, when they completed (if done),
and the last processed offset (useful for estimating progress).
"""

[route.get_table_sizes]
PATH = ["table-sizes"]
METHOD = "GET"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- Expand phase: create new BIGINT-keyed tables alongside the existing ones.
-- Old tables (hash, fee_merkle_tree, block_merkle_tree) are left untouched and
-- serve as the read fallback during the backfill window.
-- The contract phase (dropping old tables and renaming *_bigint to canonical names)
-- is a separate follow-up migration.

-- Drop reward merkle tree tables — unused and always empty across all deployments.
DROP TABLE reward_merkle_tree;
Comment on lines +7 to +8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: DROP TABLE without IF EXISTS could fail on fresh or partially-migrated databases.

If for any reason a node's database doesn't have these tables (e.g., a schema was manually modified or an earlier migration was partially applied), this migration will fail hard. Using DROP TABLE IF EXISTS would be more defensive. That said, since these tables are created by earlier numbered migrations that must have run before V1302, the practical risk is low.

DROP TABLE reward_merkle_tree_v2;

-- New hash table with BIGSERIAL.
CREATE TABLE hash_bigint (
id BIGSERIAL PRIMARY KEY,
value BYTEA NOT NULL UNIQUE
);

-- Seed the sequence above the old max so new auto-IDs never collide
-- with rows we backfill from hash (which preserve their original INT ids).
SELECT setval(
pg_get_serial_sequence('hash_bigint', 'id'),
GREATEST(COALESCE((SELECT MAX(id) FROM hash), 1), 1)
);

-- New Merkle-tree tables with BIGINT FK.
CREATE TABLE fee_merkle_tree_bigint (
path JSONB NOT NULL,
created BIGINT NOT NULL,
hash_id BIGINT NOT NULL REFERENCES hash_bigint(id),
children JSONB,
children_bitvec BIT VARYING,
idx JSONB,
entry JSONB,
PRIMARY KEY (path, created)
);
CREATE INDEX fee_merkle_tree_bigint_created ON fee_merkle_tree_bigint (created);

CREATE TABLE block_merkle_tree_bigint (
path JSONB NOT NULL,
created BIGINT NOT NULL,
hash_id BIGINT NOT NULL REFERENCES hash_bigint(id),
children JSONB,
children_bitvec BIT VARYING,
idx JSONB,
entry JSONB,
PRIMARY KEY (path, created)
);
CREATE INDEX block_merkle_tree_bigint_created ON block_merkle_tree_bigint (created);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Progress-tracking table for background DataBackfill migrations.
CREATE TABLE deferred_migrations (
name TEXT PRIMARY KEY,
started_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ,
error TEXT,
Comment on lines +4 to +6
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error column is defined here but never written to by the migration runner (MigrationRegistry).

The run_migration method logs errors via tracing::error! but never persists the error message to this column. Either populate it on failure (useful for debugging on nodes without log access) or remove the column to avoid confusion.

If keeping it: writing the error in run_migration when returning false would be a natural place:

// After logging the error, persist it:
sqlx::query("UPDATE deferred_migrations SET error = $1 WHERE name = $2")
    .bind(format!("{e:#}"))
    .bind(name)
    .execute(db.pool()) // or open a new transaction
    ...

last_offset BIGINT
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Rename tables to *_bigint for consistency with the postgres schema.
-- SQLite integers are already 64-bit so this is a naming-only change.
ALTER TABLE hash RENAME TO hash_bigint;
ALTER TABLE fee_merkle_tree RENAME TO fee_merkle_tree_bigint;
ALTER TABLE block_merkle_tree RENAME TO block_merkle_tree_bigint;

-- Drop reward merkle tree tables — unused and always empty across all deployments.
DROP TABLE IF EXISTS reward_merkle_tree;
DROP TABLE IF EXISTS reward_merkle_tree_v2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Progress-tracking table for background DataBackfill migrations.
CREATE TABLE deferred_migrations (
name TEXT PRIMARY KEY,
started_at TEXT NOT NULL,
completed_at TEXT,
error TEXT,
last_offset INTEGER
);
1 change: 1 addition & 0 deletions crates/espresso/node/api/public-env-vars.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ variables = [
"ESPRESSO_NODE_API_PEERS",
"ESPRESSO_NODE_API_PORT",
"ESPRESSO_NODE_ARCHIVE",
"ESPRESSO_NODE_BACKFILL_BATCH_DELAY_MS",
"ESPRESSO_NODE_BOOTSTRAP_EPOCH_CATCHUP_TIMEOUT",
"ESPRESSO_NODE_CATCHUP_BACKOFF_FACTOR",
"ESPRESSO_NODE_CATCHUP_BACKOFF_JITTER",
Expand Down
6 changes: 4 additions & 2 deletions crates/espresso/node/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,10 @@ where
async fn get_table_sizes(&self) -> anyhow::Result<Vec<data_source::TableSize>> {
self.inner().get_table_sizes().await
}

async fn get_migration_status(&self) -> anyhow::Result<Vec<data_source::MigrationStatus>> {
self.inner().get_migration_status().await
}
}

impl<N: ConnectedNetwork<PubKey>, P: SequencerPersistence, D: CatchupStorage + Send + Sync>
Expand Down Expand Up @@ -6021,7 +6025,6 @@ mod test {
}

#[rstest]
#[case(POS_V3)]
#[case(POS_V4)]
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn test_state_reconstruction(#[case] upgrade: Upgrade) -> anyhow::Result<()> {
Expand Down Expand Up @@ -7655,7 +7658,6 @@ mod test {
}

#[rstest]
#[case(POS_V3)]
#[case(POS_V4)]
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn test_reward_proof_endpoint(#[case] upgrade: Upgrade) -> anyhow::Result<()> {
Expand Down
14 changes: 14 additions & 0 deletions crates/espresso/node/src/api/data_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,26 @@ pub struct TableSize {
pub total_size_bytes: Option<i64>,
}

/// Status of a single deferred background migration.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MigrationStatus {
pub name: String,
pub started_at: chrono::DateTime<chrono::Utc>,
pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
pub last_offset: Option<i64>,
}

/// Data source for database metadata and statistics.
///
/// This trait is only implemented by SQL-based storage backends (PostgreSQL and SQLite).
pub(crate) trait DatabaseMetadataSource {
/// Get the sizes of all tables in the database.
fn get_table_sizes(&self) -> impl Send + Future<Output = anyhow::Result<Vec<TableSize>>>;

/// Get the status of all deferred background migrations.
fn get_migration_status(
&self,
) -> impl Send + Future<Output = anyhow::Result<Vec<MigrationStatus>>>;
}

// ============================================================================
Expand Down
11 changes: 11 additions & 0 deletions crates/espresso/node/src/api/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,17 @@ where
.map_err(|err| Error::internal(format!("failed to get table sizes: {err:#}")))
}
.boxed()
})?
.at("get_migration_status", |_req, state| {
async move {
state
.read(|state| state.get_migration_status().boxed())
.await
.map_err(|err| {
Error::internal(format!("failed to get migration status: {err:#}"))
})
}
.boxed()
})?;

Ok(api)
Expand Down
Loading
Loading