Skip to content

Commit 01b85e0

Browse files
author
Brian Cummings
committed
chore: add migration service
1 parent bcea6e4 commit 01b85e0

File tree

5 files changed

+86
-12
lines changed

5 files changed

+86
-12
lines changed

.github/workflows/main_pathfinderhonormanager.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,6 @@ jobs:
9696
name: migration-script
9797
path: PathfinderHonorManager/migration.sql
9898

99-
- name: Apply EF Migrations (Safe - Additive Only)
100-
run: |
101-
dotnet ef database update --connection "$PROD_CONNECTION_STRING"
102-
working-directory: ./PathfinderHonorManager
103-
env:
104-
PROD_CONNECTION_STRING: ${{ secrets.PRODCONNECTIONSTRING }}
105-
ConnectionStrings__PathfinderCS: ${{ secrets.PRODCONNECTIONSTRING }}
106-
10799
- name: dotnet publish
108100
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
109101

PathfinderHonorManager/Healthcheck/MigrationHealthCheck.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,27 @@ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context
2323
try
2424
{
2525
var pendingMigrations = await _context.Database.GetPendingMigrationsAsync(cancellationToken);
26+
var appliedMigrations = await _context.Database.GetAppliedMigrationsAsync(cancellationToken);
2627

2728
if (pendingMigrations.Any())
2829
{
2930
return HealthCheckResult.Degraded(
30-
"Database has pending migrations",
31+
"Database has pending migrations - migrations should be applied at startup",
3132
data: new Dictionary<string, object>
3233
{
3334
["PendingMigrations"] = pendingMigrations.ToArray(),
34-
["PendingCount"] = pendingMigrations.Count()
35+
["PendingCount"] = pendingMigrations.Count(),
36+
["AppliedCount"] = appliedMigrations.Count()
3537
});
3638
}
3739

38-
return HealthCheckResult.Healthy("Database schema is up-to-date");
40+
return HealthCheckResult.Healthy(
41+
"Database schema is up-to-date",
42+
data: new Dictionary<string, object>
43+
{
44+
["AppliedMigrations"] = appliedMigrations.Count(),
45+
["LastMigration"] = appliedMigrations.LastOrDefault() ?? "None"
46+
});
3947
}
4048
catch (Exception ex)
4149
{

PathfinderHonorManager/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Extensions.Logging.AzureAppServices;
66
using Microsoft.Extensions.Configuration;
77
using System.Diagnostics.CodeAnalysis;
8+
using PathfinderHonorManager.Service;
89

910
namespace PathfinderHonorManager
1011
{
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Hosting;
4+
using Microsoft.Extensions.Logging;
5+
using PathfinderHonorManager.DataAccess;
6+
using System;
7+
using System.Linq;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace PathfinderHonorManager.Service
12+
{
13+
public class MigrationService : IHostedService
14+
{
15+
private readonly IServiceProvider _serviceProvider;
16+
private readonly ILogger<MigrationService> _logger;
17+
18+
public MigrationService(IServiceProvider serviceProvider, ILogger<MigrationService> logger)
19+
{
20+
_serviceProvider = serviceProvider;
21+
_logger = logger;
22+
}
23+
24+
public async Task StartAsync(CancellationToken cancellationToken)
25+
{
26+
_logger.LogInformation("Starting database migration check...");
27+
28+
using var scope = _serviceProvider.CreateScope();
29+
var context = scope.ServiceProvider.GetRequiredService<PathfinderContext>();
30+
31+
try
32+
{
33+
var pendingMigrations = await context.Database.GetPendingMigrationsAsync(cancellationToken);
34+
35+
if (pendingMigrations.Any())
36+
{
37+
_logger.LogInformation("Found {Count} pending migrations. Applying...", pendingMigrations.Count());
38+
39+
foreach (var migration in pendingMigrations)
40+
{
41+
_logger.LogInformation("Pending migration: {Migration}", migration);
42+
}
43+
44+
await context.Database.MigrateAsync(cancellationToken);
45+
_logger.LogInformation("Database migrations completed successfully");
46+
}
47+
else
48+
{
49+
_logger.LogInformation("Database is up-to-date, no migrations needed");
50+
}
51+
52+
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync(cancellationToken);
53+
_logger.LogInformation("Total applied migrations: {Count}", appliedMigrations.Count());
54+
}
55+
catch (Exception ex)
56+
{
57+
_logger.LogError(ex, "Failed to apply database migrations. Application startup will be aborted.");
58+
throw new InvalidOperationException("Database migration failed during application startup. See inner exception for details.", ex);
59+
}
60+
}
61+
62+
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
63+
}
64+
}

PathfinderHonorManager/Startup.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,14 @@ public void ConfigureServices(IServiceCollection services)
135135
services
136136
.AddDbContext<PathfinderContext>(options =>
137137
options.UseNpgsql(Configuration.GetConnectionString("PathfinderCS"),
138-
npgsqlOptions => npgsqlOptions.CommandTimeout(30)));
138+
npgsqlOptions =>
139+
{
140+
npgsqlOptions.CommandTimeout(60);
141+
npgsqlOptions.EnableRetryOnFailure(
142+
maxRetryCount: 3,
143+
maxRetryDelay: TimeSpan.FromSeconds(10),
144+
errorCodesToAdd: null);
145+
}));
139146
services
140147
.AddAutoMapper(typeof(AutoMapperConfig));
141148
services
@@ -145,6 +152,8 @@ public void ConfigureServices(IServiceCollection services)
145152
.AddScoped<IClubService, ClubService>()
146153
.AddScoped<IAchievementService, AchievementService>()
147154
.AddScoped<IPathfinderAchievementService, PathfinderAchievementService>();
155+
156+
services.AddHostedService<MigrationService>();
148157
services
149158
.AddFluentValidationAutoValidation()
150159
.AddFluentValidationClientsideAdapters()

0 commit comments

Comments
 (0)