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()
.UseStartup<Startup>()
.Build();
_Host.Start();
_Host.StartWithTasksAsync().GetAwaiter().GetResult();
var urls = _Host.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
foreach (var url in urls)

View file

@ -34,11 +34,29 @@ using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using NBXplorer.DerivationStrategy;
using System.Net;
using Microsoft.AspNetCore.Hosting;
namespace BTCPayServer
{
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)
{
StringBuilder builder = new StringBuilder();

View file

@ -78,6 +78,7 @@ namespace BTCPayServer.Hosting
services.TryAddSingleton<InvoicePaymentNotification>();
services.TryAddSingleton<BTCPayServerOptions>(o =>
o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
services.AddStartupTask<MigrationStartupTask>();
services.TryAddSingleton<InvoiceRepository>(o =>
{
var opts = o.GetRequiredService<BTCPayServerOptions>();
@ -184,7 +185,6 @@ namespace BTCPayServer.Hosting
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase)));
});
services.AddSingleton<IHostedService, CssThemeManagerHostedService>();
services.AddSingleton<IHostedService, MigratorHostedService>();
services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>();
@ -316,37 +316,9 @@ namespace BTCPayServer.Hosting
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>();
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.Stores;
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 StoreRepository _StoreRepository;
private BTCPayNetworkProvider _NetworkProvider;
private SettingsRepository _Settings;
public MigratorHostedService(
public MigrationStartupTask(
BTCPayNetworkProvider networkProvider,
StoreRepository storeRepository,
ApplicationDbContextFactory dbContextFactory,
@ -28,18 +30,11 @@ namespace BTCPayServer.HostedServices
_NetworkProvider = networkProvider;
_Settings = settingsRepository;
}
internal override Task[] InitializeTasks()
{
return new[]
{
Update()
};
}
private async Task Update()
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
try
{
await Migrate(cancellationToken);
var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings();
if (!settings.DeprecatedLightningConnectionStringCheck)
{
@ -80,11 +75,34 @@ namespace BTCPayServer.HostedServices
}
catch (Exception ex)
{
Logs.PayServer.LogError(ex, "Error on the MigratorHostedService");
Logs.PayServer.LogError(ex, "Error on the MigrationStartupTask");
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()
{
bool save = false;

View file

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