Use Migration startup task when starting BTCPay instead of hosted service.

This commit is contained in:
nicolas.dorier 2019-07-08 12:12:39 +09:00
parent 3c4455c23c
commit 36046f08f7
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
6 changed files with 65 additions and 44 deletions

View file

@ -148,7 +148,7 @@ namespace BTCPayServer.Tests
.UseKestrel() .UseKestrel()
.UseStartup<Startup>() .UseStartup<Startup>()
.Build(); .Build();
_Host.Start(); _Host.StartWithTasksAsync().GetAwaiter().GetResult();
var urls = _Host.ServerFeatures.Get<IServerAddressesFeature>().Addresses; var urls = _Host.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
foreach (var url in urls) foreach (var url in urls)

View file

@ -34,11 +34,29 @@ using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using NBXplorer.DerivationStrategy; using NBXplorer.DerivationStrategy;
using System.Net; using System.Net;
using Microsoft.AspNetCore.Hosting;
namespace BTCPayServer namespace BTCPayServer
{ {
public static class Extensions public static class Extensions
{ {
public static IServiceCollection AddStartupTask<T>(this IServiceCollection services)
where T : class, IStartupTask
=> services.AddTransient<IStartupTask, T>();
public static async Task StartWithTasksAsync(this IWebHost webHost, CancellationToken cancellationToken = default)
{
// Load all tasks from DI
var startupTasks = webHost.Services.GetServices<IStartupTask>();
// Execute all the tasks
foreach (var startupTask in startupTasks)
{
await startupTask.ExecuteAsync(cancellationToken).ConfigureAwait(false);
}
// Start the tasks as normal
await webHost.StartAsync(cancellationToken).ConfigureAwait(false);
}
public static string PrettyPrint(this TimeSpan expiration) public static string PrettyPrint(this TimeSpan expiration)
{ {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();

View file

@ -78,6 +78,7 @@ namespace BTCPayServer.Hosting
services.TryAddSingleton<InvoicePaymentNotification>(); services.TryAddSingleton<InvoicePaymentNotification>();
services.TryAddSingleton<BTCPayServerOptions>(o => services.TryAddSingleton<BTCPayServerOptions>(o =>
o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value); o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
services.AddStartupTask<MigrationStartupTask>();
services.TryAddSingleton<InvoiceRepository>(o => services.TryAddSingleton<InvoiceRepository>(o =>
{ {
var opts = o.GetRequiredService<BTCPayServerOptions>(); var opts = o.GetRequiredService<BTCPayServerOptions>();
@ -184,7 +185,6 @@ namespace BTCPayServer.Hosting
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase))); o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase)));
}); });
services.AddSingleton<IHostedService, CssThemeManagerHostedService>(); services.AddSingleton<IHostedService, CssThemeManagerHostedService>();
services.AddSingleton<IHostedService, MigratorHostedService>();
services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>(); services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>();
@ -316,37 +316,9 @@ namespace BTCPayServer.Hosting
public static IApplicationBuilder UsePayServer(this IApplicationBuilder app) public static IApplicationBuilder UsePayServer(this IApplicationBuilder app)
{ {
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
//Wait the DB is ready
Retry(() =>
{
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
});
}
app.UseMiddleware<BTCPayMiddleware>(); app.UseMiddleware<BTCPayMiddleware>();
return app; return app;
} }
static void Retry(Action act)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
while (true)
{
try
{
act();
return;
}
// Starting up
catch (PostgresException ex) when (ex.SqlState == "57P03") { Thread.Sleep(1000); }
catch when (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
}
}
}
} }

View file

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace BTCPayServer
{
public interface IStartupTask
{
Task ExecuteAsync(CancellationToken cancellationToken = default);
}
}

View file

@ -8,16 +8,18 @@ using BTCPayServer.Data;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using System.Threading;
using Npgsql;
namespace BTCPayServer.HostedServices namespace BTCPayServer
{ {
public class MigratorHostedService : BaseAsyncService public class MigrationStartupTask : IStartupTask
{ {
private ApplicationDbContextFactory _DBContextFactory; private ApplicationDbContextFactory _DBContextFactory;
private StoreRepository _StoreRepository; private StoreRepository _StoreRepository;
private BTCPayNetworkProvider _NetworkProvider; private BTCPayNetworkProvider _NetworkProvider;
private SettingsRepository _Settings; private SettingsRepository _Settings;
public MigratorHostedService( public MigrationStartupTask(
BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider networkProvider,
StoreRepository storeRepository, StoreRepository storeRepository,
ApplicationDbContextFactory dbContextFactory, ApplicationDbContextFactory dbContextFactory,
@ -28,18 +30,11 @@ namespace BTCPayServer.HostedServices
_NetworkProvider = networkProvider; _NetworkProvider = networkProvider;
_Settings = settingsRepository; _Settings = settingsRepository;
} }
internal override Task[] InitializeTasks() public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
return new[]
{
Update()
};
}
private async Task Update()
{ {
try try
{ {
await Migrate(cancellationToken);
var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings(); var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings();
if (!settings.DeprecatedLightningConnectionStringCheck) if (!settings.DeprecatedLightningConnectionStringCheck)
{ {
@ -80,11 +75,34 @@ namespace BTCPayServer.HostedServices
} }
catch (Exception ex) catch (Exception ex)
{ {
Logs.PayServer.LogError(ex, "Error on the MigratorHostedService"); Logs.PayServer.LogError(ex, "Error on the MigrationStartupTask");
throw; throw;
} }
} }
private async Task Migrate(CancellationToken cancellationToken)
{
using (CancellationTokenSource timeout = new CancellationTokenSource(10_000))
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken))
{
retry:
try
{
await _DBContextFactory.CreateContext().Database.MigrateAsync();
}
// Starting up
catch when (!cts.Token.IsCancellationRequested)
{
try
{
await Task.Delay(1000, cts.Token);
}
catch { }
goto retry;
}
}
}
private async Task ConvertConvertWalletKeyPathRoots() private async Task ConvertConvertWalletKeyPathRoots()
{ {
bool save = false; bool save = false;

View file

@ -68,7 +68,7 @@ namespace BTCPayServer
}) })
.UseStartup<Startup>() .UseStartup<Startup>()
.Build(); .Build();
host.StartAsync().GetAwaiter().GetResult(); host.StartWithTasksAsync().GetAwaiter().GetResult();
var urls = host.ServerFeatures.Get<IServerAddressesFeature>().Addresses; var urls = host.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
foreach (var url in urls) foreach (var url in urls)
{ {