1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 21:02:54 +08:00

Allow realm migration to run again if interrupted halfway

This commit is contained in:
Dean Herbert 2022-01-24 18:55:15 +09:00
parent 37b74aafcc
commit 66c5d77d63
2 changed files with 140 additions and 133 deletions

View File

@ -27,7 +27,9 @@ namespace osu.Game.Database
{
internal class EFToRealmMigrator : CompositeDrawable
{
public bool FinishedMigrating { get; private set; }
public Task<bool> MigrationCompleted => migrationCompleted.Task;
private readonly TaskCompletionSource<bool> migrationCompleted = new TaskCompletionSource<bool>();
[Resolved]
private DatabaseContextFactory efContextFactory { get; set; } = null!;
@ -99,6 +101,17 @@ namespace osu.Game.Database
{
using (var ef = efContextFactory.Get())
{
realmContextFactory.Write(realm =>
{
// Before beginning, ensure realm is in an empty state.
// Migrations which are half-completed could lead to issues if the user tries a second time.
// Note that we only do this for beatmaps and scores since the other migrations are yonks old.
realm.RemoveAll<BeatmapSetInfo>();
realm.RemoveAll<BeatmapInfo>();
realm.RemoveAll<BeatmapMetadata>();
realm.RemoveAll<ScoreInfo>();
});
migrateSettings(ef);
migrateSkins(ef);
migrateBeatmaps(ef);
@ -114,7 +127,7 @@ namespace osu.Game.Database
Logger.Log("Your development database has been fully migrated to realm. If you switch back to a pre-realm branch and need your previous database, rename the backup file back to \"client.db\".\n\nNote that doing this can potentially leave your file store in a bad state.", level: LogLevel.Important);
}, TaskCreationOptions.LongRunning).ContinueWith(t =>
{
FinishedMigrating = true;
migrationCompleted.SetResult(true);
});
}
@ -149,14 +162,6 @@ namespace osu.Game.Database
{
log($"Found {count} beatmaps in EF");
// only migrate data if the realm database is empty.
// note that this cannot be written as: `realm.All<BeatmapSetInfo>().All(s => s.Protected)`, because realm does not support `.All()`.
if (realm.All<BeatmapSetInfo>().Any(s => !s.Protected))
{
log("Skipping migration as realm already has beatmaps loaded");
}
else
{
var transaction = realm.BeginWrite();
int written = 0;
@ -229,7 +234,6 @@ namespace osu.Game.Database
}
log($"Successfully migrated {count} beatmaps to realm");
}
});
}
@ -280,13 +284,6 @@ namespace osu.Game.Database
{
log($"Found {count} scores in EF");
// only migrate data if the realm database is empty.
if (realm.All<ScoreInfo>().Any())
{
log("Skipping migration as realm already has scores loaded");
}
else
{
var transaction = realm.BeginWrite();
int written = 0;
@ -343,7 +340,6 @@ namespace osu.Game.Database
}
log($"Successfully migrated {count} scores to realm");
}
});
}

View File

@ -74,11 +74,22 @@ namespace osu.Game.Screens
base.OnEntering(last);
LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal);
LoadComponentAsync(loadableScreen = CreateLoadableScreen());
// A non-null context factory means there's still content to migrate.
if (efContextFactory != null)
{
LoadComponentAsync(realmMigrator = new EFToRealmMigrator(), AddInternal);
realmMigrator.MigrationCompleted.ContinueWith(_ => Schedule(() =>
{
// Delay initial screen loading to ensure that the migration is in a complete and sane state
// before the intro screen may import the game intro beatmap.
LoadComponentAsync(loadableScreen = CreateLoadableScreen());
}));
}
else
{
LoadComponentAsync(loadableScreen = CreateLoadableScreen());
}
LoadComponentAsync(spinner = new LoadingSpinner(true, true)
{
@ -96,7 +107,7 @@ namespace osu.Game.Screens
private void checkIfLoaded()
{
if (loadableScreen.LoadState != LoadState.Ready || !precompiler.FinishedCompiling || realmMigrator?.FinishedMigrating == false)
if (loadableScreen?.LoadState != LoadState.Ready || !precompiler.FinishedCompiling)
{
Schedule(checkIfLoaded);
return;