Files
git.stella-ops.org/src/StellaOps.Vexer.Storage.Mongo/Migrations/VexMongoMigrationRunner.cs

75 lines
2.8 KiB
C#

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
namespace StellaOps.Vexer.Storage.Mongo.Migrations;
internal sealed class VexMongoMigrationRunner
{
private readonly IMongoDatabase _database;
private readonly IReadOnlyList<IVexMongoMigration> _migrations;
private readonly ILogger<VexMongoMigrationRunner> _logger;
public VexMongoMigrationRunner(
IMongoDatabase database,
IEnumerable<IVexMongoMigration> migrations,
ILogger<VexMongoMigrationRunner> logger)
{
_database = database ?? throw new ArgumentNullException(nameof(database));
_migrations = (migrations ?? throw new ArgumentNullException(nameof(migrations)))
.OrderBy(migration => migration.Id, StringComparer.Ordinal)
.ToArray();
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async ValueTask RunAsync(CancellationToken cancellationToken)
{
if (_migrations.Count == 0)
{
return;
}
var migrationsCollection = _database.GetCollection<VexMigrationRecord>(VexMongoCollectionNames.Migrations);
await EnsureMigrationsIndexAsync(migrationsCollection, cancellationToken).ConfigureAwait(false);
var applied = await LoadAppliedMigrationsAsync(migrationsCollection, cancellationToken).ConfigureAwait(false);
foreach (var migration in _migrations)
{
if (applied.Contains(migration.Id))
{
continue;
}
_logger.LogInformation("Applying Vexer Mongo migration {MigrationId}", migration.Id);
await migration.ExecuteAsync(_database, cancellationToken).ConfigureAwait(false);
var record = new VexMigrationRecord(migration.Id, DateTimeOffset.UtcNow);
await migrationsCollection.InsertOneAsync(record, cancellationToken: cancellationToken).ConfigureAwait(false);
_logger.LogInformation("Completed Vexer Mongo migration {MigrationId}", migration.Id);
}
}
private static ValueTask EnsureMigrationsIndexAsync(
IMongoCollection<VexMigrationRecord> collection,
CancellationToken cancellationToken)
{
// default _id index already enforces uniqueness
return ValueTask.CompletedTask;
}
private static async ValueTask<HashSet<string>> LoadAppliedMigrationsAsync(
IMongoCollection<VexMigrationRecord> collection,
CancellationToken cancellationToken)
{
var records = await collection.Find(FilterDefinition<VexMigrationRecord>.Empty)
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
return records.Select(static record => record.Id)
.ToHashSet(StringComparer.Ordinal);
}
}