From 081d8a8e53087ed6c0972ec199c5f4d2dd1e8664 Mon Sep 17 00:00:00 2001 From: Qstick Date: Tue, 13 Feb 2018 19:07:26 +0100 Subject: [PATCH] Added: Backup improvements from Sonarr (#2513) Fixes #2440 --- .../System/Backup/BackupModule.cs | 6 +- src/NzbDrone.Core/Backup/Backup.cs | 2 +- src/NzbDrone.Core/Backup/BackupCommand.cs | 13 +++- src/NzbDrone.Core/Backup/BackupService.cs | 20 +++---- .../Backup/MakeDatabaseBackup.cs | 59 +++++++++++++++++++ src/NzbDrone.Core/Datastore/Database.cs | 3 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + 7 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs diff --git a/src/NzbDrone.Api/System/Backup/BackupModule.cs b/src/NzbDrone.Api/System/Backup/BackupModule.cs index b5074793e..8874ad420 100644 --- a/src/NzbDrone.Api/System/Backup/BackupModule.cs +++ b/src/NzbDrone.Api/System/Backup/BackupModule.cs @@ -21,9 +21,9 @@ public List GetBackupFiles() return backups.Select(b => new BackupResource { - Id = b.Path.GetHashCode(), - Name = Path.GetFileName(b.Path), - Path = b.Path, + Id = b.Name.GetHashCode(), + Name = b.Name, + Path = $"/backup/{b.Type.ToString().ToLower()}/{b.Name}", Type = b.Type, Time = b.Time }).ToList(); diff --git a/src/NzbDrone.Core/Backup/Backup.cs b/src/NzbDrone.Core/Backup/Backup.cs index a4505d991..5d148648e 100644 --- a/src/NzbDrone.Core/Backup/Backup.cs +++ b/src/NzbDrone.Core/Backup/Backup.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Core.Backup { public class Backup { - public string Path { get; set; } + public string Name { get; set; } public BackupType Type { get; set; } public DateTime Time { get; set; } } diff --git a/src/NzbDrone.Core/Backup/BackupCommand.cs b/src/NzbDrone.Core/Backup/BackupCommand.cs index 3a852cf7a..1f5550e59 100644 --- a/src/NzbDrone.Core/Backup/BackupCommand.cs +++ b/src/NzbDrone.Core/Backup/BackupCommand.cs @@ -4,7 +4,18 @@ namespace NzbDrone.Core.Backup { public class BackupCommand : Command { - public BackupType Type { get; set; } + public BackupType Type + { + get + { + if (Trigger == CommandTrigger.Scheduled) + { + return BackupType.Scheduled; + } + + return BackupType.Manual; + } + } public override bool SendUpdatesToClient => true; diff --git a/src/NzbDrone.Core/Backup/BackupService.cs b/src/NzbDrone.Core/Backup/BackupService.cs index 39130703f..6556f648f 100644 --- a/src/NzbDrone.Core/Backup/BackupService.cs +++ b/src/NzbDrone.Core/Backup/BackupService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.SQLite; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -25,6 +26,7 @@ public interface IBackupService public class BackupService : IBackupService, IExecute { private readonly IMainDatabase _maindDb; + private readonly IMakeDatabaseBackup _makeDatabaseBackup; private readonly IDiskTransferService _diskTransferService; private readonly IDiskProvider _diskProvider; private readonly IAppFolderInfo _appFolderInfo; @@ -36,6 +38,7 @@ public class BackupService : IBackupService, IExecute private static readonly Regex BackupFileRegex = new Regex(@"radarr_backup_[._0-9]+\.zip", RegexOptions.Compiled | RegexOptions.IgnoreCase); public BackupService(IMainDatabase maindDb, + IMakeDatabaseBackup makeDatabaseBackup, IDiskTransferService diskTransferService, IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, @@ -43,6 +46,7 @@ public BackupService(IMainDatabase maindDb, Logger logger) { _maindDb = maindDb; + _makeDatabaseBackup = makeDatabaseBackup; _diskTransferService = diskTransferService; _diskProvider = diskProvider; _appFolderInfo = appFolderInfo; @@ -68,7 +72,7 @@ public void Backup(BackupType backupType) { CleanupOldBackups(backupType); } - + BackupConfigFile(); BackupDatabase(); @@ -89,7 +93,7 @@ public List GetBackups() { backups.AddRange(GetBackupFiles(folder).Select(b => new Backup { - Path = Path.GetFileName(b), + Name = Path.GetFileName(b), Type = backupType, Time = _diskProvider.FileGetLastWrite(b) })); @@ -111,17 +115,7 @@ private void BackupDatabase() { _logger.ProgressDebug("Backing up database"); - using (var unitOfWork = new UnitOfWork(() => _maindDb.GetDataMapper())) - { - unitOfWork.BeginTransaction(IsolationLevel.Serializable); - - var databaseFile = _appFolderInfo.GetNzbDroneDatabase(); - var tempDatabaseFile = Path.Combine(_backupTempFolder, Path.GetFileName(databaseFile)); - - _diskTransferService.TransferFile(databaseFile, tempDatabaseFile, TransferMode.Copy); - - unitOfWork.Commit(); - } + _makeDatabaseBackup.BackupDatabase(_maindDb, _backupTempFolder); } private void BackupConfigFile() diff --git a/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs new file mode 100644 index 000000000..bafcd8232 --- /dev/null +++ b/src/NzbDrone.Core/Backup/MakeDatabaseBackup.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.IO; +using System.Linq; +using System.Text; +using NLog; +using NzbDrone.Core.Datastore; +using System.Data; + +namespace NzbDrone.Core.Backup +{ + public interface IMakeDatabaseBackup + { + void BackupDatabase(IDatabase database, string targetDirectory); + } + + public class MakeDatabaseBackup : IMakeDatabaseBackup + { + private readonly Logger _logger; + + public MakeDatabaseBackup(Logger logger) + { + _logger = logger; + } + + public void BackupDatabase(IDatabase database, string targetDirectory) + { + var sourceConnectionString = database.GetDataMapper().ConnectionString; + var backupConnectionStringBuilder = new SQLiteConnectionStringBuilder(sourceConnectionString); + + backupConnectionStringBuilder.DataSource = Path.Combine(targetDirectory, Path.GetFileName(backupConnectionStringBuilder.DataSource)); + // We MUST use journal mode instead of WAL coz WAL has issues when page sizes change. This should also automatically deal with the -journal and -wal files during restore. + backupConnectionStringBuilder.JournalMode = SQLiteJournalModeEnum.Truncate; + + using (var sourceConnection = (SQLiteConnection)SQLiteFactory.Instance.CreateConnection()) + using (var backupConnection = (SQLiteConnection)SQLiteFactory.Instance.CreateConnection()) + { + sourceConnection.ConnectionString = sourceConnectionString; + backupConnection.ConnectionString = backupConnectionStringBuilder.ToString(); + + sourceConnection.Open(); + backupConnection.Open(); + + sourceConnection.BackupDatabase(backupConnection, "main", "main", -1, null, 500); + + // The backup changes the journal_mode, force it to truncate again. + using (var command = backupConnection.CreateCommand()) + { + command.CommandText = "pragma journal_mode=truncate"; + command.ExecuteNonQuery(); + } + + // Make sure there are no lingering connections. + SQLiteConnection.ClearAllPools(); + } + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Database.cs b/src/NzbDrone.Core/Datastore/Database.cs index c4e59f983..991cd9b0e 100644 --- a/src/NzbDrone.Core/Datastore/Database.cs +++ b/src/NzbDrone.Core/Datastore/Database.cs @@ -25,7 +25,6 @@ public Database(string databaseName, Func datamapperFactory) _datamapperFactory = datamapperFactory; } - public IDataMapper GetDataMapper() { return _datamapperFactory(); @@ -54,4 +53,4 @@ public void Vacuum() } } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 748c66a6d..9fa82b371 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -187,6 +187,7 @@ +