Fix migration of Invoice's payment (#6241)

This commit is contained in:
Nicolas Dorier
2024-09-23 23:59:18 +09:00
committed by GitHub
parent 3cf1aa00fa
commit 1d9ec253fb
5 changed files with 59 additions and 40 deletions

View File

@@ -210,7 +210,7 @@ namespace BTCPayServer.Data
}
blob.ConvertNumberToString("price");
Currency = blob["currency"].Value<string>();
Currency = blob["currency"].Value<string>().ToUpperInvariant();
var isTopup = blob["type"]?.Value<string>() is "TopUp";
var amount = decimal.Parse(blob["price"].Value<string>(), CultureInfo.InvariantCulture);
Amount = isTopup && amount == 0 ? null : decimal.Parse(blob["price"].Value<string>(), CultureInfo.InvariantCulture);

View File

@@ -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<string>();
MigratedPaymentMethodId = PaymentMethodId;
PaymentMethodId = cryptoCode + "_" + blob["cryptoPaymentDataType"].Value<string>();
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)

View File

@@ -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();

View File

@@ -29,7 +29,7 @@ public abstract class BlobMigratorHostedService<TEntity> : 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<TEntity> : 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<TEntity> : IHostedService
else
Logs.LogInformation("Migrating from the beginning");
int batchSize = BatchSize;
retry:
while (!cancellationToken.IsCancellationRequested)
{
retry:
List<TEntity> 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<TEntity> 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<Settings>(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<Settings>(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>(settings, SettingsKey);
}
catch (Exception ex)
{
Logs.LogError(ex, "Error while migrating");
goto retry;
}
settings = new Settings() { Progress = progress };
await SettingsRepository.UpdateSetting<Settings>(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;
}
}

View File

@@ -71,6 +71,12 @@ public class InvoiceBlobMigratorHostedService : BlobMigratorHostedService<Invoic
paymentEntity.Details = JToken.FromObject(handler.ParsePaymentDetails(paymentEntity.Details), handler.Serializer);
}
pay.SetBlob(paymentEntity);
if (pay.PaymentMethodId != pay.MigratedPaymentMethodId)
{
ctx.Entry(pay).State = EntityState.Added;
ctx.Payments.Remove(new PaymentData() { Id = pay.Id, PaymentMethodId = pay.MigratedPaymentMethodId });
}
}
}
return invoices[^1].Created;