Summary
On a consensus DB whose data tables have not been created yet, schema migration aborts with TableDoesNotExist("certificates") and the node cannot start. Two of the four per-table migration functions open their table in a read transaction (which errors on a missing table), while the other two use a write transaction (which creates it) — so the behaviour is inconsistent and the read-transaction path is fatal.
Affected code
crates/consensus-db/src/migrations.rs
migrate_certificates and migrate_decided_blocks open the table in a read transaction:
let mut next_height = if let Some((min_height, _)) = self
.db
.begin_read()?
.open_table(CERTIFICATES_TABLE)? // -> TableError::TableDoesNotExist if absent
.first()?
redb's open_table on a read transaction returns TableError::TableDoesNotExist when the table was never created. By contrast, migrate_undecided_blocks and migrate_pending_parts open their table in a write transaction, which creates it, so they already treat a missing table as empty. The two read-transaction functions abort the entire migration instead.
How it is reached
The tables do not always exist when migration runs:
Db::new runs the migration, and only afterwards does Store::open call create_tables.
needs_migration treats "database file exists, no schema version recorded" as v0 and requests a migration.
redb::Database::create creates the file before that schema version is written.
So a first start interrupted between (1) creating the file and (3) writing the version leaves a database file with a metadata table but no data tables. Every subsequent start then runs the migration, hits the read-transaction open_table, and fails. arc-node-consensus db migrate fails the same way, including --dry-run (which goes through preview_migrate → the same functions).
Observed error
Error: Table(TableDoesNotExist("certificates"))
The node is stuck: it cannot start and cannot migrate, even though the datadir is recoverable.
Proposed fix
Make migrate_certificates and migrate_decided_blocks treat a missing table as empty — matching what migrate_undecided_blocks / migrate_pending_parts already do — by handling TableError::TableDoesNotExist as "no rows" instead of propagating it.
Verification
I have a patch plus a regression test that reproduces the failure. On main the test fails with Table(TableDoesNotExist("certificates")); with the fix it passes, and the full arc-consensus-db suite (107 tests) stays green (cargo fmt/clippy clean). Happy to open a PR referencing this issue once assigned.
Environment
- Reproduced against the current
main on x86_64-unknown-linux-gnu, Rust 1.93.0.
Summary
On a consensus DB whose data tables have not been created yet, schema migration aborts with
TableDoesNotExist("certificates")and the node cannot start. Two of the four per-table migration functions open their table in a read transaction (which errors on a missing table), while the other two use a write transaction (which creates it) — so the behaviour is inconsistent and the read-transaction path is fatal.Affected code
crates/consensus-db/src/migrations.rsmigrate_certificatesandmigrate_decided_blocksopen the table in a read transaction:redb'sopen_tableon a read transaction returnsTableError::TableDoesNotExistwhen the table was never created. By contrast,migrate_undecided_blocksandmigrate_pending_partsopen their table in a write transaction, which creates it, so they already treat a missing table as empty. The two read-transaction functions abort the entire migration instead.How it is reached
The tables do not always exist when migration runs:
Db::newruns the migration, and only afterwards doesStore::opencallcreate_tables.needs_migrationtreats "database file exists, no schema version recorded" asv0and requests a migration.redb::Database::createcreates the file before that schema version is written.So a first start interrupted between (1) creating the file and (3) writing the version leaves a database file with a metadata table but no data tables. Every subsequent start then runs the migration, hits the read-transaction
open_table, and fails.arc-node-consensus db migratefails the same way, including--dry-run(which goes throughpreview_migrate→ the same functions).Observed error
The node is stuck: it cannot start and cannot migrate, even though the datadir is recoverable.
Proposed fix
Make
migrate_certificatesandmigrate_decided_blockstreat a missing table as empty — matching whatmigrate_undecided_blocks/migrate_pending_partsalready do — by handlingTableError::TableDoesNotExistas "no rows" instead of propagating it.Verification
I have a patch plus a regression test that reproduces the failure. On
mainthe test fails withTable(TableDoesNotExist("certificates")); with the fix it passes, and the fullarc-consensus-dbsuite (107 tests) stays green (cargo fmt/clippyclean). Happy to open a PR referencing this issue once assigned.Environment
mainonx86_64-unknown-linux-gnu, Rust 1.93.0.