added database recovery on app start.
This commit is contained in:
parent
653f45f10e
commit
130be58f8c
|
@ -5,7 +5,6 @@ using System.Linq;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common;
|
|
||||||
using NzbDrone.Core.Jobs;
|
using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.Providers;
|
using NzbDrone.Core.Providers;
|
||||||
using NzbDrone.Core.Providers.ExternalNotification;
|
using NzbDrone.Core.Providers.ExternalNotification;
|
||||||
|
@ -18,12 +17,12 @@ namespace NzbDrone.Core.Test
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
class CentralDispatchFixture : CoreTest
|
class CentralDispatchFixture : CoreTest
|
||||||
{
|
{
|
||||||
readonly IList<Type> indexers = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(IndexerBase))).ToList();
|
readonly IList<string> indexers = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(IndexerBase))).Select(c => c.ToString()).ToList();
|
||||||
readonly IList<Type> jobs = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IJob))).ToList();
|
readonly IList<string> jobs = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IJob))).Select(c=>c.ToString()).ToList();
|
||||||
readonly IList<Type> extNotifications = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(ExternalNotificationBase))).ToList();
|
readonly IList<Type> extNotifications = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(ExternalNotificationBase))).ToList();
|
||||||
readonly IList<Type> metadata = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(MetadataBase))).ToList();
|
readonly IList<Type> metadata = typeof(CentralDispatch).Assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(MetadataBase))).ToList();
|
||||||
|
|
||||||
private IContainer kernel;
|
private readonly IContainer kernel;
|
||||||
|
|
||||||
public CentralDispatchFixture()
|
public CentralDispatchFixture()
|
||||||
{
|
{
|
||||||
|
@ -57,8 +56,6 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void All_jobs_should_be_registered()
|
public void All_jobs_should_be_registered()
|
||||||
{
|
{
|
||||||
//Assert
|
|
||||||
|
|
||||||
var registeredJobs = kernel.Resolve<IEnumerable<IJob>>();
|
var registeredJobs = kernel.Resolve<IEnumerable<IJob>>();
|
||||||
|
|
||||||
jobs.Should().NotBeEmpty();
|
jobs.Should().NotBeEmpty();
|
||||||
|
@ -69,8 +66,6 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void All_indexers_should_be_registered()
|
public void All_indexers_should_be_registered()
|
||||||
{
|
{
|
||||||
//Assert
|
|
||||||
|
|
||||||
var registeredIndexers = kernel.Resolve<IEnumerable<IndexerBase>>();
|
var registeredIndexers = kernel.Resolve<IEnumerable<IndexerBase>>();
|
||||||
|
|
||||||
indexers.Should().NotBeEmpty();
|
indexers.Should().NotBeEmpty();
|
||||||
|
@ -81,8 +76,6 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void All_externalNotifiers_should_be_registered()
|
public void All_externalNotifiers_should_be_registered()
|
||||||
{
|
{
|
||||||
//Assert
|
|
||||||
|
|
||||||
var externalNotificationBases = kernel.Resolve<IEnumerable<ExternalNotificationBase>>();
|
var externalNotificationBases = kernel.Resolve<IEnumerable<ExternalNotificationBase>>();
|
||||||
|
|
||||||
extNotifications.Should().NotBeEmpty();
|
extNotifications.Should().NotBeEmpty();
|
||||||
|
@ -93,8 +86,6 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void All_metadata_clients_should_be_registered()
|
public void All_metadata_clients_should_be_registered()
|
||||||
{
|
{
|
||||||
//Assert
|
|
||||||
|
|
||||||
var metadataBases = kernel.Resolve<IEnumerable<MetadataBase>>();
|
var metadataBases = kernel.Resolve<IEnumerable<MetadataBase>>();
|
||||||
|
|
||||||
metadata.Should().NotBeEmpty();
|
metadata.Should().NotBeEmpty();
|
||||||
|
@ -111,7 +102,7 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void indexers_are_initialized()
|
public void indexers_are_initialized()
|
||||||
{
|
{
|
||||||
kernel.Resolve<IndexerProvider>().All().Should().HaveSameCount(indexers);
|
kernel.Resolve<IndexerProvider>().All().Select(c => c.IndexProviderType).Should().BeEquivalentTo(indexers);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -129,7 +120,7 @@ namespace NzbDrone.Core.Test
|
||||||
[Test]
|
[Test]
|
||||||
public void quality_profile_initialized()
|
public void quality_profile_initialized()
|
||||||
{
|
{
|
||||||
kernel.Resolve<QualityProvider>().All().Should().HaveCount(2);
|
kernel.Resolve<QualityProvider>().All().Should().HaveCount(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
|
@ -32,9 +32,9 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
|
|
||||||
File.Copy(DB_TEMPLATE_NAME, fileName);
|
File.Copy(DB_TEMPLATE_NAME, fileName);
|
||||||
|
|
||||||
ConnectionString = Connection.GetConnectionString(fileName);
|
ConnectionString = ConnectionFactory.GetConnectionString(fileName);
|
||||||
|
|
||||||
var database = Connection.GetPetaPocoDb(ConnectionString);
|
var database = ConnectionFactory.GetPetaPocoDb(ConnectionString);
|
||||||
|
|
||||||
Console.WriteLine("====================DataBase====================");
|
Console.WriteLine("====================DataBase====================");
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
@ -46,8 +46,8 @@ namespace NzbDrone.Core.Test.Framework
|
||||||
internal static void CreateDataBaseTemplate()
|
internal static void CreateDataBaseTemplate()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Creating an empty PetaPoco database");
|
Console.WriteLine("Creating an empty PetaPoco database");
|
||||||
var connectionString = Connection.GetConnectionString(DB_TEMPLATE_NAME);
|
var connectionString = ConnectionFactory.GetConnectionString(DB_TEMPLATE_NAME);
|
||||||
var database = Connection.GetPetaPocoDb(connectionString);
|
var database = ConnectionFactory.GetPetaPocoDb(connectionString);
|
||||||
database.Dispose();
|
database.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ namespace NzbDrone.Core
|
||||||
|
|
||||||
private static void RegisterAssembly(this ContainerBuilder container, Assembly assembly)
|
private static void RegisterAssembly(this ContainerBuilder container, Assembly assembly)
|
||||||
{
|
{
|
||||||
|
logger.Info("Registering Services from {0}", assembly.FullName);
|
||||||
|
|
||||||
container.RegisterAssemblyTypes(assembly)
|
container.RegisterAssemblyTypes(assembly)
|
||||||
.AsSelf()
|
.AsSelf()
|
||||||
.SingleInstance();
|
.SingleInstance();
|
||||||
|
@ -66,14 +68,14 @@ namespace NzbDrone.Core
|
||||||
var appDataPath = new EnvironmentProvider().GetAppDataPath();
|
var appDataPath = new EnvironmentProvider().GetAppDataPath();
|
||||||
if (!Directory.Exists(appDataPath)) Directory.CreateDirectory(appDataPath);
|
if (!Directory.Exists(appDataPath)) Directory.CreateDirectory(appDataPath);
|
||||||
|
|
||||||
container.Register(c => c.Resolve<Connection>().GetMainPetaPocoDb())
|
container.Register(c => c.Resolve<ConnectionFactory>().GetMainPetaPocoDb())
|
||||||
.As<IDatabase>();
|
.As<IDatabase>();
|
||||||
|
|
||||||
container.Register(c => c.Resolve<Connection>().GetLogPetaPocoDb(false))
|
container.Register(c => c.Resolve<ConnectionFactory>().GetLogPetaPocoDb(false))
|
||||||
.SingleInstance()
|
.SingleInstance()
|
||||||
.Named<IDatabase>("DatabaseTarget");
|
.Named<IDatabase>("DatabaseTarget");
|
||||||
|
|
||||||
container.Register(c => c.Resolve<Connection>().GetLogPetaPocoDb())
|
container.Register(c => c.Resolve<ConnectionFactory>().GetLogPetaPocoDb())
|
||||||
.Named<IDatabase>("LogProvider");
|
.Named<IDatabase>("LogProvider");
|
||||||
|
|
||||||
container.RegisterType<DatabaseTarget>().WithParameter(ResolvedParameter.ForNamed<IDatabase>("DatabaseTarget"));
|
container.RegisterType<DatabaseTarget>().WithParameter(ResolvedParameter.ForNamed<IDatabase>("DatabaseTarget"));
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Configuration;
|
|
||||||
using System.Data.Common;
|
|
||||||
using System.Data.SqlServerCe;
|
|
||||||
using NzbDrone.Common;
|
|
||||||
using NzbDrone.Core.Instrumentation;
|
|
||||||
using PetaPoco;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
|
||||||
{
|
|
||||||
public class Connection
|
|
||||||
{
|
|
||||||
private readonly EnvironmentProvider _environmentProvider;
|
|
||||||
|
|
||||||
static Connection()
|
|
||||||
{
|
|
||||||
Database.Mapper = new CustomeMapper();
|
|
||||||
|
|
||||||
var dataSet = ConfigurationManager.GetSection("system.data") as System.Data.DataSet;
|
|
||||||
dataSet.Tables[0].Rows.Add("Microsoft SQL Server Compact Data Provider 4.0"
|
|
||||||
, "System.Data.SqlServerCe.4.0"
|
|
||||||
, ".NET Framework Data Provider for Microsoft SQL Server Compact"
|
|
||||||
, "System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Connection(EnvironmentProvider environmentProvider)
|
|
||||||
{
|
|
||||||
_environmentProvider = environmentProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String MainConnectionString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetConnectionString(_environmentProvider.GetNzbDroneDbFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String LogConnectionString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return GetConnectionString(_environmentProvider.GetLogDbFileDbFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetConnectionString(string path)
|
|
||||||
{
|
|
||||||
return String.Format("Data Source=\"{0}\"; Max Database Size = 512;", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDatabase GetMainPetaPocoDb(Boolean profiled = true)
|
|
||||||
{
|
|
||||||
return GetPetaPocoDb(MainConnectionString, profiled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDatabase GetLogPetaPocoDb(Boolean profiled = true)
|
|
||||||
{
|
|
||||||
return GetPetaPocoDb(LogConnectionString, profiled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDatabase GetPetaPocoDb(string connectionString, Boolean profiled = true)
|
|
||||||
{
|
|
||||||
MigrationsHelper.Run(connectionString, true);
|
|
||||||
|
|
||||||
var factory = new DbProviderFactory
|
|
||||||
{
|
|
||||||
IsProfiled = profiled
|
|
||||||
};
|
|
||||||
|
|
||||||
var db = new Database(connectionString, factory, Database.DBType.SqlServerCE)
|
|
||||||
{
|
|
||||||
KeepConnectionAlive = true,
|
|
||||||
ForceDateTimesToUtc = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return db;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data.SqlServerCe;
|
||||||
|
using System.IO;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using PetaPoco;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore
|
||||||
|
{
|
||||||
|
public class ConnectionFactory
|
||||||
|
{
|
||||||
|
private readonly EnvironmentProvider _environmentProvider;
|
||||||
|
private static readonly Logger logger = LogManager.GetLogger("ConnectionFactory");
|
||||||
|
|
||||||
|
static ConnectionFactory()
|
||||||
|
{
|
||||||
|
Database.Mapper = new CustomeMapper();
|
||||||
|
|
||||||
|
var dataSet = (System.Data.DataSet)ConfigurationManager.GetSection("system.data");
|
||||||
|
dataSet.Tables[0].Rows.Add("Microsoft SQL Server Compact Data Provider 4.0"
|
||||||
|
, "System.Data.SqlServerCe.4.0"
|
||||||
|
, ".NET Framework Data Provider for Microsoft SQL Server Compact"
|
||||||
|
, "System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionFactory(EnvironmentProvider environmentProvider)
|
||||||
|
{
|
||||||
|
_environmentProvider = environmentProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String MainConnectionString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetConnectionString(_environmentProvider.GetNzbDroneDbFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String LogConnectionString
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetConnectionString(_environmentProvider.GetLogDbFileDbFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetConnectionString(string path)
|
||||||
|
{
|
||||||
|
return String.Format("Data Source=\"{0}\"; Max Database Size = 512;", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDatabase GetMainPetaPocoDb(Boolean profiled = true)
|
||||||
|
{
|
||||||
|
return GetPetaPocoDb(MainConnectionString, profiled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDatabase GetLogPetaPocoDb(Boolean profiled = true)
|
||||||
|
{
|
||||||
|
return GetPetaPocoDb(LogConnectionString, profiled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static readonly HashSet<String> initilized = new HashSet<string>();
|
||||||
|
|
||||||
|
|
||||||
|
public static IDatabase GetPetaPocoDb(string connectionString, Boolean profiled = true)
|
||||||
|
{
|
||||||
|
lock (initilized)
|
||||||
|
{
|
||||||
|
if (!initilized.Contains(connectionString))
|
||||||
|
{
|
||||||
|
VerifyDatabase(connectionString);
|
||||||
|
MigrationsHelper.Run(connectionString, true);
|
||||||
|
initilized.Add(connectionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = new DbProviderFactory
|
||||||
|
{
|
||||||
|
IsProfiled = profiled
|
||||||
|
};
|
||||||
|
|
||||||
|
var db = new Database(connectionString, factory, Database.DBType.SqlServerCE)
|
||||||
|
{
|
||||||
|
KeepConnectionAlive = true,
|
||||||
|
ForceDateTimesToUtc = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void VerifyDatabase(string connectionString)
|
||||||
|
{
|
||||||
|
logger.Debug("Verifying database {0}", connectionString);
|
||||||
|
|
||||||
|
var sqlConnection = new SqlCeConnection(connectionString);
|
||||||
|
|
||||||
|
if (!File.Exists(sqlConnection.Database))
|
||||||
|
{
|
||||||
|
logger.Debug("database file doesn't exist. {0}", sqlConnection.Database);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var sqlEngine = new SqlCeEngine(connectionString))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sqlEngine.Verify(VerifyOption.Default))
|
||||||
|
{
|
||||||
|
logger.Debug("Database integrity verified.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Database verification failed.");
|
||||||
|
RepairDatabase(connectionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RepairDatabase(string connectionString)
|
||||||
|
{
|
||||||
|
logger.Info("Attempting to repair database: {0}", connectionString);
|
||||||
|
using (var sqlEngine = new SqlCeEngine(connectionString))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sqlEngine.Repair(connectionString, RepairOption.RecoverAllOrFail);
|
||||||
|
logger.Info("Recovery was successful without any data loss {0}", connectionString);
|
||||||
|
}
|
||||||
|
catch (SqlCeException e)
|
||||||
|
{
|
||||||
|
logger.WarnException(
|
||||||
|
"Safe recovery failed. will attempts a more aggressive strategy. might case loss of data.",
|
||||||
|
e);
|
||||||
|
sqlEngine.Repair(connectionString, RepairOption.DeleteCorruptedRows);
|
||||||
|
logger.Warn("Database was recovered. some data might have been lost");
|
||||||
|
|
||||||
|
//TODO: do db cleanup to avoid broken relationships.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,13 +11,9 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
public static readonly Dictionary<String, String> _migrated = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
public static void Run(string connetionString, bool trace)
|
public static void Run(string connetionString, bool trace)
|
||||||
{
|
{
|
||||||
if (_migrated.ContainsKey(connetionString)) return;
|
|
||||||
_migrated.Add(connetionString, string.Empty);
|
|
||||||
|
|
||||||
EnsureDatabase(connetionString);
|
EnsureDatabase(connetionString);
|
||||||
|
|
||||||
Logger.Trace("Preparing to run database migration");
|
Logger.Trace("Preparing to run database migration");
|
||||||
|
@ -37,9 +33,6 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
|
|
||||||
migrator.MigrateToLastVersion();
|
migrator.MigrateToLastVersion();
|
||||||
|
|
||||||
//ForceSubSonicMigration(Connection.CreateSimpleRepository(connetionString));
|
|
||||||
|
|
||||||
Logger.Info("Database migration completed");
|
Logger.Info("Database migration completed");
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +40,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.FatalException("An error has occurred while migrating database", e);
|
Logger.FatalException("An error has occurred while migrating database", e);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Constants.cs" />
|
<Compile Include="Constants.cs" />
|
||||||
<Compile Include="ContainerExtentions.cs" />
|
<Compile Include="ContainerExtentions.cs" />
|
||||||
<Compile Include="Datastore\Connection.cs" />
|
<Compile Include="Datastore\ConnectionFactory.cs" />
|
||||||
<Compile Include="Datastore\MigrationLogger.cs" />
|
<Compile Include="Datastore\MigrationLogger.cs" />
|
||||||
<Compile Include="Datastore\MigrationsHelper.cs" />
|
<Compile Include="Datastore\MigrationsHelper.cs" />
|
||||||
<Compile Include="Datastore\CustomeMapper.cs" />
|
<Compile Include="Datastore\CustomeMapper.cs" />
|
||||||
|
|
Loading…
Reference in New Issue