Handle SQLite corruption during database migration, take 2

I recommend reviewing this with whitespace changes disabled.

If a database migration fails, we want to do two things:

1. Check if it failed due to database corruption. If so, flag the
   database as corrupted to enable recovery.
2. Crash.

We were only doing the second part because each individual migration
would crash instead of throwing the error.

The logic was effectively this:

    do {
      for migration in migrations {
        do {
          try migration.run()
        } catch {
          crash()
        }
      }
    } catch {
      // This `catch` would never happen because a failed migration
      // would crash, not throw.
      flagDatabaseCorruptionIfNecessary(error)
      crash()
    }

Now it's this:

    do {
      for migration in migrations {
        try migration.run()
      }
    } catch {
      flagDatabaseCorruptionIfNecessary(error)
      crash()
    }

See 3892b9ead6, which was my first attempt
at doing this.

Also see <https://github.com/signalapp/Signal-iOS/issues/5481> for
people experiencing this issue.
This commit is contained in:
Evan Hahn 2023-01-27 11:37:37 -06:00 committed by Evan Hahn
parent 935e0b4559
commit d36a7f58c0

View File

@ -363,21 +363,17 @@ public class GRDBSchemaMigrator: NSObject {
}
Logger.info("Running migration: \(identifier)")
do {
let transaction = GRDBWriteTransaction(database: database)
let result = try migrate(transaction)
switch result {
case .success:
let timeElapsed = CACurrentMediaTime() - startTime
let formattedTime = String(format: "%0.2fms", timeElapsed * 1000)
Logger.info("Migration completed: \(identifier), duration: \(formattedTime)")
case .failure(let error):
owsFail("Migration \(identifier) failed with returned error: \(error)")
}
transaction.finalizeTransaction()
} catch {
owsFail("Migration \(identifier) failed with thrown error: \(error)")
let transaction = GRDBWriteTransaction(database: database)
let result = try migrate(transaction)
switch result {
case .success:
let timeElapsed = CACurrentMediaTime() - startTime
let formattedTime = String(format: "%0.2fms", timeElapsed * 1000)
Logger.info("Migration completed: \(identifier), duration: \(formattedTime)")
case .failure(let error):
throw error
}
transaction.finalizeTransaction()
}
}