mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Show failed migrations instead of waiting on them forever
This commit is contained in:
@@ -44,6 +44,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
- Fixed the app closing when WebView's page process crashed ([@AntsyLich](https://github.com/AntsyLich))
|
||||
- Fixed installing or updating an extension sometimes cancelling the install of another one ([@AntsyLich](https://github.com/AntsyLich))
|
||||
- Fixed updating from v0.20.x taking up to hours on libraries with many duplicate entries ([@AntsyLich](https://github.com/AntsyLich))
|
||||
- Fixed the app staying on the updating screen for good when updating its data failed, it now shows what went wrong, and updating failing on leftover data of deleted entries ([@AntsyLich](https://github.com/AntsyLich))
|
||||
|
||||
## [v0.21.0] - 2026-10-12
|
||||
### Added
|
||||
|
||||
@@ -179,6 +179,9 @@ class MainActivity : BaseActivity() {
|
||||
Migrator.awaitAndRelease()
|
||||
isAppMigrating.value = false
|
||||
}
|
||||
// Uncaught on purpose: a failed database migration otherwise leaves the migrating screen up for good,
|
||||
// while the crash screen tells what went wrong and lets the logs be shared
|
||||
lifecycleScope.launch { databaseRepository.awaitOpen() }
|
||||
|
||||
// Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079
|
||||
if (!isTaskRoot) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package mihon.core.migration
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
class MigrationJobFactory(
|
||||
@@ -20,7 +22,18 @@ class MigrationJobFactory(
|
||||
}
|
||||
async {
|
||||
val prev = acc.await()
|
||||
migration(migrationContext) || prev
|
||||
// A failure would cancel the rest of the chain and leave the app waiting on it for good
|
||||
val ran = try {
|
||||
migration(migrationContext)
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e) {
|
||||
"Migration failed: { name = ${migration::class.simpleName}, version = ${migration.version} }"
|
||||
}
|
||||
false
|
||||
}
|
||||
ran || prev
|
||||
}
|
||||
} else {
|
||||
logcat {
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
package mihon.data.database
|
||||
|
||||
import app.cash.sqldelight.db.QueryResult
|
||||
import app.cash.sqldelight.db.SqlDriver
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.Inject
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import mihon.domain.database.repository.DatabaseRepository
|
||||
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DatabaseRepositoryImpl : DatabaseRepository {
|
||||
class DatabaseRepositoryImpl(
|
||||
// Lazy since the driver reports its migration state here
|
||||
private val driver: Lazy<SqlDriver>,
|
||||
) : DatabaseRepository {
|
||||
|
||||
override val isMigrating = MutableStateFlow(false)
|
||||
|
||||
override suspend fun awaitOpen() {
|
||||
withContext(Dispatchers.IO) {
|
||||
driver.value.executeQuery(null, "SELECT 1", { QueryResult.Value(Unit) }, 0).await()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,19 @@ import kotlin.String;
|
||||
import kotlin.collections.List;
|
||||
import kotlinx.serialization.json.JsonObject;
|
||||
|
||||
-- ---------------------------------------------------------------------------------------------------------
|
||||
-- orphans
|
||||
-- ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Rows pointing at an entry, chapter or category that no longer exists were written while foreign keys weren't
|
||||
-- enforced. Nothing can reach them, and copied over they'd fail the foreign key check that ends the migration.
|
||||
DELETE FROM chapters WHERE manga_id NOT IN (SELECT _id FROM mangas);
|
||||
DELETE FROM history WHERE chapter_id NOT IN (SELECT _id FROM chapters);
|
||||
DELETE FROM mangas_categories
|
||||
WHERE manga_id NOT IN (SELECT _id FROM mangas) OR category_id NOT IN (SELECT _id FROM categories);
|
||||
DELETE FROM manga_sync WHERE manga_id NOT IN (SELECT _id FROM mangas);
|
||||
DELETE FROM excluded_scanlators WHERE manga_id NOT IN (SELECT _id FROM mangas);
|
||||
|
||||
-- ---------------------------------------------------------------------------------------------------------
|
||||
-- table - manga
|
||||
-- ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -9,4 +9,9 @@ interface DatabaseRepository {
|
||||
* app update.
|
||||
*/
|
||||
val isMigrating: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Opens the database, migrating it if needed, and throws what made the migration fail.
|
||||
*/
|
||||
suspend fun awaitOpen()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user