From 6c2731071dc9e3ced467d50bf18fa93b645320c4 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Wed, 19 Jan 2022 15:03:47 -0800 Subject: [PATCH] Migration Safety (#967) * Updated EF version * When we perform a migration, backup the database to temp/migration/VERSION and do it only once in case a migration fails. * When a migration fails, we will now restore what was corrupted. --- API/API.csproj | 6 +++--- API/Program.cs | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/API/API.csproj b/API/API.csproj index fbf15067e..759f1ae86 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -49,13 +49,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/API/Program.cs b/API/Program.cs index 4ed8ce56a..286a0f0cd 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -62,7 +62,15 @@ namespace API if (pendingMigrations.Any()) { logger.LogInformation("Performing backup as migrations are needed. Backup will be kavita.db in temp folder"); - directoryService.CopyFileToDirectory(directoryService.FileSystem.Path.Join(directoryService.ConfigDirectory, "kavita.db"), directoryService.TempDirectory); + var migrationDirectory = await GetMigrationDirectory(context, directoryService); + directoryService.ExistOrCreate(migrationDirectory); + + if (!directoryService.FileSystem.File.Exists( + directoryService.FileSystem.Path.Join(migrationDirectory, "kavita.db"))) + { + directoryService.CopyFileToDirectory(directoryService.FileSystem.Path.Join(directoryService.ConfigDirectory, "kavita.db"), migrationDirectory); + logger.LogInformation("Database backed up to {MigrationDirectory}", migrationDirectory); + } } await context.Database.MigrateAsync(); @@ -82,12 +90,42 @@ namespace API catch (Exception ex) { var logger = services.GetRequiredService>(); - logger.LogCritical(ex, "An error occurred during migration"); + var context = services.GetRequiredService(); + var migrationDirectory = await GetMigrationDirectory(context, directoryService); + + logger.LogCritical(ex, "A migration failed during startup. Restoring backup from {MigrationDirectory} and exiting", migrationDirectory); + directoryService.CopyFileToDirectory(directoryService.FileSystem.Path.Join(migrationDirectory, "kavita.db"), directoryService.ConfigDirectory); + + return; } await host.RunAsync(); } + private static async Task GetMigrationDirectory(DataContext context, IDirectoryService directoryService) + { + string currentVersion = null; + try + { + currentVersion = + (await context.ServerSetting.SingleOrDefaultAsync(s => + s.Key == ServerSettingKey.InstallVersion))?.Value; + } + catch + { + // ignored + } + + if (string.IsNullOrEmpty(currentVersion)) + { + currentVersion = "vUnknown"; + } + + var migrationDirectory = directoryService.FileSystem.Path.Join(directoryService.TempDirectory, + "migration", currentVersion); + return migrationDirectory; + } + private static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) =>