Add support for backwards migrations

This commit is contained in:
Jordan Brown
2018-11-24 15:48:53 -05:00
parent 3b3a449537
commit 92ffd94158
5 changed files with 88 additions and 2 deletions
@@ -12,7 +12,7 @@ using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components
{
/// <inheritdoc />
sealed class InstanceManager : IInstanceManager, IHostedService, IDisposable
sealed class InstanceManager : IInstanceManager, IRestartHandler, IHostedService, IDisposable
{
/// <summary>
/// The <see cref="IInstanceFactory"/> for the <see cref="InstanceManager"/>
@@ -49,6 +49,11 @@ namespace Tgstation.Server.Host.Components
/// </summary>
readonly Dictionary<long, IInstance> instances;
/// <summary>
/// Used in <see cref="StopAsync(CancellationToken)"/> to determine if database downgrades must be made
/// </summary>
Version downgradeVersion;
/// <summary>
/// If the <see cref="InstanceManager"/> has been <see cref="Dispose"/>d
/// </summary>
@@ -62,16 +67,21 @@ namespace Tgstation.Server.Host.Components
/// <param name="databaseContextFactory">The value of <paramref name="databaseContextFactory"/></param>
/// <param name="application">The value of <see cref="application"/></param>
/// <param name="jobManager">The value of <see cref="jobManager"/></param>
/// <param name="serverControl">The <see cref="IServerControl"/> used to register the <see cref="InstanceManager"/> as a <see cref="IRestartHandler"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, ILogger<InstanceManager> logger)
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
{
this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.application = application ?? throw new ArgumentNullException(nameof(application));
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
if (serverControl == null)
throw new ArgumentNullException(nameof(serverControl));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
serverControl.RegisterForRestart(this);
instances = new Dictionary<long, IInstance>();
}
@@ -225,6 +235,17 @@ namespace Tgstation.Server.Host.Components
await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
//downgrade the db if necessary
if (downgradeVersion != null)
await databaseContextFactory.UseContext(db => db.SchemaDowngradeForServerVersion(downgradeVersion, cancellationToken)).ConfigureAwait(false);
}
/// <inheritdoc />
public Task HandleRestart(Version updateVersion, CancellationToken cancellationToken)
{
downgradeVersion = updateVersion != null && updateVersion < application.Version ? updateVersion : null;
return Task.CompletedTask;
}
}
}
@@ -1,7 +1,11 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -169,5 +173,51 @@ namespace Tgstation.Server.Host.Models
/// <inheritdoc />
public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
/// <summary>
/// If the MY_ class of migrations should be used instead of the MS_ class
/// </summary>
/// <returns><see langword="true"/> if the MY_ class of migrations should be used instead of the MS_ class, <see langword="false"/> otherwise</returns>
protected abstract bool UseMySQLMigrations();
/// <inheritdoc />
public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
if (version < new Version(4, 0))
throw new ArgumentOutOfRangeException(nameof(version), version, "Not a valid V4 version!");
string targetMigration = null;
//Update this with new migrations as they are made
//Always use the MS class
//TODO: Uncomment once #816 is merged
/*
if (version < new Version(4, 0, 2))
targetMigration = nameof(MSReattachCompileJobRequired);
*/
if (targetMigration == null)
return;
if (UseMySQLMigrations())
targetMigration = String.Format(CultureInfo.InvariantCulture, "MY" + targetMigration.Substring(2));
//even though it clearly implements it in the DatabaseFacade definition this won't work without casting (╯ಠ益ಠ)╯︵ ┻━┻
var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration);
try
{
await migrator.MigrateAsync(targetMigration, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
Logger.LogCritical("Failed to migrate! Exception: {0}", e);
}
}
}
}
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -87,5 +88,13 @@ namespace Tgstation.Server.Host.Models
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Initialize(CancellationToken cancellationToken);
/// <summary>
/// Attempt to downgrade the schema to the migration used for a given server <paramref name="version"/>
/// </summary>
/// <param name="version">The tgstation-server <see cref="Version"/> that the schema should downgrade for</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken);
}
}
@@ -38,5 +38,8 @@ namespace Tgstation.Server.Host.Models
else
options.UseMySql(DatabaseConfiguration.ConnectionString);
}
/// <inheritdoc />
protected override bool UseMySQLMigrations() => true;
}
}
@@ -26,5 +26,8 @@ namespace Tgstation.Server.Host.Models
base.OnConfiguring(options);
options.UseSqlServer(DatabaseConfiguration.ConnectionString);
}
/// <inheritdoc />
protected override bool UseMySQLMigrations() => false;
}
}