mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-30 08:33:19 +01:00
Add support for backwards migrations
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user