From 1d9ec253fb04d0f943f752af1c6ea4ed30cc0ed6 Mon Sep 17 00:00:00 2001 From: Nicolas Dorier Date: Mon, 23 Sep 2024 23:59:18 +0900 Subject: [PATCH] Fix migration of Invoice's payment (#6241) --- .../Data/InvoiceData.Migration.cs | 2 +- .../Data/PaymentData.Migration.cs | 6 ++ BTCPayServer.Tests/TestAccount.cs | 2 +- .../BlobMigratorHostedService.cs | 83 ++++++++++--------- .../InvoiceBlobMigratorHostedService.cs | 6 ++ 5 files changed, 59 insertions(+), 40 deletions(-) diff --git a/BTCPayServer.Data/Data/InvoiceData.Migration.cs b/BTCPayServer.Data/Data/InvoiceData.Migration.cs index aea620db9..843f71203 100644 --- a/BTCPayServer.Data/Data/InvoiceData.Migration.cs +++ b/BTCPayServer.Data/Data/InvoiceData.Migration.cs @@ -210,7 +210,7 @@ namespace BTCPayServer.Data } blob.ConvertNumberToString("price"); - Currency = blob["currency"].Value(); + Currency = blob["currency"].Value().ToUpperInvariant(); var isTopup = blob["type"]?.Value() is "TopUp"; var amount = decimal.Parse(blob["price"].Value(), CultureInfo.InvariantCulture); Amount = isTopup && amount == 0 ? null : decimal.Parse(blob["price"].Value(), CultureInfo.InvariantCulture); diff --git a/BTCPayServer.Data/Data/PaymentData.Migration.cs b/BTCPayServer.Data/Data/PaymentData.Migration.cs index 8f9d29499..63fb86e61 100644 --- a/BTCPayServer.Data/Data/PaymentData.Migration.cs +++ b/BTCPayServer.Data/Data/PaymentData.Migration.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; using BTCPayServer.Migrations; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using NBitcoin; using NBitcoin.Altcoins; using NBitcoin.DataEncoders; @@ -44,6 +46,7 @@ namespace BTCPayServer.Data } var cryptoCode = blob["cryptoCode"].Value(); + MigratedPaymentMethodId = PaymentMethodId; PaymentMethodId = cryptoCode + "_" + blob["cryptoPaymentDataType"].Value(); PaymentMethodId = MigrationExtensions.MigratePaymentMethodId(PaymentMethodId); var divisibility = MigrationExtensions.GetDivisibility(PaymentMethodId); @@ -163,6 +166,9 @@ namespace BTCPayServer.Data } [NotMapped] public bool Migrated { get; set; } + [NotMapped] + [EditorBrowsable(EditorBrowsableState.Never)] + public string MigratedPaymentMethodId { get; set; } static readonly DateTimeOffset unixRef = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); public static long DateTimeToMilliUnixTime(in DateTime time) diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index 72168ace8..a26f33f90 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -704,7 +704,7 @@ retry: { var localPayment = invoice.Replace("3sgUCCtUBg6S8LJkrbdfAWbsJMqByFLfvSqjG6xKBWEd", storeId); // Old data could have Type to null. - localPayment += "BTC-CHAIN"; + localPayment += "UNKNOWN"; await writer.WriteLineAsync(localPayment); } await writer.FlushAsync(); diff --git a/BTCPayServer/HostedServices/BlobMigratorHostedService.cs b/BTCPayServer/HostedServices/BlobMigratorHostedService.cs index e6221a014..8a62bed94 100644 --- a/BTCPayServer/HostedServices/BlobMigratorHostedService.cs +++ b/BTCPayServer/HostedServices/BlobMigratorHostedService.cs @@ -29,7 +29,7 @@ public abstract class BlobMigratorHostedService : IHostedService public bool Complete { get; set; } } Task? _Migrating; - TaskCompletionSource _Cts = new TaskCompletionSource(); + CancellationTokenSource? _Cts; public BlobMigratorHostedService( ILogger logs, ISettingsRepository settingsRepository, @@ -46,7 +46,8 @@ public abstract class BlobMigratorHostedService : IHostedService public Task StartAsync(CancellationToken cancellationToken) { - _Migrating = Migrate(cancellationToken); + _Cts = new CancellationTokenSource(); + _Migrating = Migrate(_Cts.Token); return Task.CompletedTask; } public int BatchSize { get; set; } = 1000; @@ -61,47 +62,57 @@ public abstract class BlobMigratorHostedService : IHostedService else Logs.LogInformation("Migrating from the beginning"); + int batchSize = BatchSize; +retry: while (!cancellationToken.IsCancellationRequested) { -retry: - List entities; - DateTimeOffset progress; - await using (var ctx = ApplicationDbContextFactory.CreateContext(o => o.CommandTimeout((int)TimeSpan.FromDays(1.0).TotalSeconds))) + try { - var query = GetQuery(ctx, settings?.Progress).Take(batchSize); - entities = await query.ToListAsync(cancellationToken); - if (entities.Count == 0) + List entities; + DateTimeOffset progress; + await using (var ctx = ApplicationDbContextFactory.CreateContext(o => o.CommandTimeout((int)TimeSpan.FromDays(1.0).TotalSeconds))) { - var count = await GetQuery(ctx, null).CountAsync(cancellationToken); - if (count != 0) + var query = GetQuery(ctx, settings?.Progress).Take(batchSize); + entities = await query.ToListAsync(cancellationToken); + if (entities.Count == 0) { - settings = new Settings() { Progress = null }; - Logs.LogWarning("Corruption detected, reindexing the table..."); - await Reindex(ctx, cancellationToken); + var count = await GetQuery(ctx, null).CountAsync(cancellationToken); + if (count != 0) + { + settings = new Settings() { Progress = null }; + Logs.LogWarning("Corruption detected, reindexing the table..."); + await Reindex(ctx, cancellationToken); + goto retry; + } + await SettingsRepository.UpdateSetting(new Settings() { Complete = true }, SettingsKey); + Logs.LogInformation("Migration completed"); + await PostMigrationCleanup(ctx, cancellationToken); + return; + } + + try + { + progress = ProcessEntities(ctx, entities); + await ctx.SaveChangesAsync(); + batchSize = BatchSize; + } + catch (Exception ex) when (ex is DbUpdateConcurrencyException or TimeoutException or OperationCanceledException) + { + batchSize /= 2; + batchSize = Math.Max(1, batchSize); goto retry; } - await SettingsRepository.UpdateSetting(new Settings() { Complete = true }, SettingsKey); - Logs.LogInformation("Migration completed"); - await PostMigrationCleanup(ctx, cancellationToken); - return; } - try - { - progress = ProcessEntities(ctx, entities); - await ctx.SaveChangesAsync(); - batchSize = BatchSize; - } - catch (Exception ex) when (ex is DbUpdateConcurrencyException or TimeoutException or OperationCanceledException) - { - batchSize /= 2; - batchSize = Math.Max(1, batchSize); - goto retry; - } + settings = new Settings() { Progress = progress }; + await SettingsRepository.UpdateSetting(settings, SettingsKey); + } + catch (Exception ex) + { + Logs.LogError(ex, "Error while migrating"); + goto retry; } - settings = new Settings() { Progress = progress }; - await SettingsRepository.UpdateSetting(settings, SettingsKey); } } @@ -121,11 +132,7 @@ retry: public Task StopAsync(CancellationToken cancellationToken) { - _Cts.TrySetCanceled(); - return (_Migrating ?? Task.CompletedTask).ContinueWith(t => - { - if (t.IsFaulted) - Logs.LogError(t.Exception, "Error while migrating"); - }); + _Cts?.Cancel(); + return _Migrating ?? Task.CompletedTask; } } diff --git a/BTCPayServer/HostedServices/InvoiceBlobMigratorHostedService.cs b/BTCPayServer/HostedServices/InvoiceBlobMigratorHostedService.cs index 27e9ed76e..00c74d472 100644 --- a/BTCPayServer/HostedServices/InvoiceBlobMigratorHostedService.cs +++ b/BTCPayServer/HostedServices/InvoiceBlobMigratorHostedService.cs @@ -71,6 +71,12 @@ public class InvoiceBlobMigratorHostedService : BlobMigratorHostedService