| | | 1 | | using Anichron.Worker.Settings; |
| | | 2 | | |
| | | 3 | | namespace Anichron.Worker.Startup; |
| | | 4 | | |
| | 6 | 5 | | public sealed partial class DatabaseMigratorService( |
| | 6 | 6 | | IServiceScopeFactory scopeFactory, |
| | 6 | 7 | | ILogger<DatabaseMigratorService> logger) : IHostedService |
| | | 8 | | { |
| | | 9 | | internal TimeSpan RetryDelay { get; init; } = TimeSpan.FromSeconds(WorkerDefaults.Migrator.RetryDelaySeconds); |
| | | 10 | | |
| | | 11 | | public async Task StartAsync(CancellationToken cancellationToken) |
| | 5 | 12 | | { |
| | 68 | 13 | | for (var attempt = 1; attempt <= WorkerDefaults.Migrator.MaxAttempts; attempt++) |
| | 34 | 14 | | { |
| | | 15 | | try |
| | 34 | 16 | | { |
| | 34 | 17 | | await using var scope = scopeFactory.CreateAsyncScope(); |
| | 34 | 18 | | var migrator = scope.ServiceProvider.GetRequiredService<IDatabaseMigrator>(); |
| | 34 | 19 | | await migrator.MigrateAsync(cancellationToken); |
| | 2 | 20 | | Log.MigrationApplied(logger); |
| | 2 | 21 | | return; |
| | 0 | 22 | | } |
| | 32 | 23 | | catch (Exception ex) when (attempt < WorkerDefaults.Migrator.MaxAttempts) |
| | 29 | 24 | | { |
| | 29 | 25 | | Log.DatabaseNotReady(logger, ex, attempt, WorkerDefaults.Migrator.MaxAttempts, (int)RetryDelay.TotalSeco |
| | 29 | 26 | | await Task.Delay(RetryDelay, cancellationToken); |
| | 29 | 27 | | } |
| | 3 | 28 | | catch (Exception ex) |
| | 3 | 29 | | { |
| | 3 | 30 | | Log.DatabaseUnavailable(logger, ex, WorkerDefaults.Migrator.MaxAttempts); |
| | 3 | 31 | | throw new InvalidOperationException( |
| | 3 | 32 | | $"Database unavailable after {WorkerDefaults.Migrator.MaxAttempts} attempts. Aborting.", |
| | 3 | 33 | | ex); |
| | | 34 | | } |
| | 29 | 35 | | } |
| | 2 | 36 | | } |
| | | 37 | | |
| | 1 | 38 | | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; |
| | | 39 | | |
| | | 40 | | private static partial class Log |
| | | 41 | | { |
| | | 42 | | [LoggerMessage(Level = LogLevel.Information, Message = "Database migration applied successfully.")] |
| | | 43 | | public static partial void MigrationApplied(ILogger logger); |
| | | 44 | | |
| | | 45 | | [LoggerMessage(Level = LogLevel.Warning, Message = "Database not ready (attempt {Attempt}/{Max}). Retrying in {D |
| | | 46 | | public static partial void DatabaseNotReady(ILogger logger, Exception ex, int attempt, int max, int delay); |
| | | 47 | | |
| | | 48 | | [LoggerMessage(Level = LogLevel.Error, Message = "Database unavailable after {Max} attempts. Aborting.")] |
| | | 49 | | public static partial void DatabaseUnavailable(ILogger logger, Exception ex, int max); |
| | | 50 | | } |
| | | 51 | | } |