Skip to content

Commit

Permalink
fix(iroh-sync): prevent panic in namespace migration (#1775)
Browse files Browse the repository at this point in the history
## Description

The migration as commited with #1770 panics when starting iroh with an
existing data dir. The reason is that redb panics when deleting table in
a transaction where they were opened as well. See:
cberner/redb#715

This fixes this by moving the table deletion to a separate transaction.

The limitation and potential panic in redb was fixed in
cberner/redb#716 so once we upgrade to the next
(still unreleased) version of redb, this can be removed again.

## Change checklist

- [x] Self-review.
- [x] Documentation updates if relevant.
  • Loading branch information
Frando authored Nov 7, 2023
1 parent 9a16498 commit 61e5924
Showing 1 changed file with 26 additions and 7 deletions.
33 changes: 26 additions & 7 deletions iroh-sync/src/store/fs/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use super::{
/// Run all database migrations, if needed.
pub fn run_migrations(db: &Database) -> Result<()> {
run_migration(db, migration_001_populate_latest_table)?;
run_migration(db, migration_002_namespaces_v2)?;
run_migration(db, migration_003_populate_by_key_index)?;
run_migration(db, migration_002_namespaces_populate_v2)?;
run_migration(db, migration_003_namespaces_delete_v1)?;
run_migration(db, migration_004_populate_by_key_index)?;
Ok(())
}

Expand Down Expand Up @@ -74,12 +75,11 @@ fn migration_001_populate_latest_table(tx: &WriteTransaction) -> Result<MigrateO
Ok(MigrateOutcome::Execute(len))
}

/// Migrate the namespaces table from V1 to V2.
fn migration_002_namespaces_v2(tx: &WriteTransaction) -> Result<MigrateOutcome> {
/// Copy the namespaces data from V1 to V2.
fn migration_002_namespaces_populate_v2(tx: &WriteTransaction) -> Result<MigrateOutcome> {
let namespaces_v1_exists = tx
.list_tables()?
.any(|handle| handle.name() == NAMESPACES_TABLE_V1.name());
// migration 002: update namespaces from V1 to V2
if !namespaces_v1_exists {
return Ok(MigrateOutcome::Skip);
}
Expand All @@ -95,12 +95,31 @@ fn migration_002_namespaces_v2(tx: &WriteTransaction) -> Result<MigrateOutcome>
namespaces_v2.insert(&id, (raw_kind, &raw_bytes))?;
entries += 1;
}
tx.delete_table(NAMESPACES_TABLE_V1)?;
Ok(MigrateOutcome::Execute(entries))
}

/// Delete the v1 namespaces table.
///
/// This should be part of [`migration_002_namespaces_populate_v2`] but due to a limitation in
/// [`redb`] up to v1.3.0 a table cannot be deleted in a transaction that also opens this table.
/// Therefore the table deletion has to be in a separate transaction.
///
/// This limitation was removed in <https://github.com/cberner/redb/pull/716> so this can be merged
/// back into [`migration_002_namespaces_populate_v2`] once we upgrade to the next redb version
/// after 1.3.
fn migration_003_namespaces_delete_v1(tx: &WriteTransaction) -> Result<MigrateOutcome> {
let namespaces_v1_exists = tx
.list_tables()?
.any(|handle| handle.name() == NAMESPACES_TABLE_V1.name());
if !namespaces_v1_exists {
return Ok(MigrateOutcome::Skip);
}
tx.delete_table(NAMESPACES_TABLE_V1)?;
Ok(MigrateOutcome::Execute(1))
}

/// migration 003: populate the by_key index table(which did not exist before)
fn migration_003_populate_by_key_index(tx: &WriteTransaction) -> Result<MigrateOutcome> {
fn migration_004_populate_by_key_index(tx: &WriteTransaction) -> Result<MigrateOutcome> {
let mut by_key_table = tx.open_table(RECORDS_BY_KEY_TABLE)?;
let records_table = tx.open_table(RECORDS_TABLE)?;
if !by_key_table.is_empty()? || records_table.is_empty()? {
Expand Down

0 comments on commit 61e5924

Please sign in to comment.