mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
* Remove only dependency on Dbriize (TextSearch in new invoice column) * Switch to table for invoice text search * Adding missing using after rebase * Removing database migration in preparation for refresh * Database Migration: Adding InvoiceSearchData * Refactoring InvoicesRepository to make AddToTextSearch static and non-async Operation as async is too expensive for simple filtering and AddRange * Renaming InvoiceQuery property to Take More inline with what property does by convention, Take is used in conjuction with Skip * Refactoring SettingsRepository so update of settings can happen in another context * Adding DbMigrationsHostedService that performs long running data migrations * Commenting special placing of MigrationStartupTask * Simplifying code and leaving comment on expected flow * Resolving problems after merge * Database Migration: Refreshing database migration, ensuring no unintended changes on ModelSnapshot Co-authored-by: rockstardev <rockstardev@users.noreply.github.com> Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
107 lines
4.6 KiB
C#
107 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Threading.Tasks;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Services;
|
|
using BTCPayServer.Services.Invoices;
|
|
|
|
namespace BTCPayServer.HostedServices
|
|
{
|
|
/// <summary>
|
|
/// In charge of all long running db migrations that we can't execute on startup in MigrationStartupTask
|
|
/// </summary>
|
|
public class DbMigrationsHostedService : BaseAsyncService
|
|
{
|
|
private readonly InvoiceRepository _invoiceRepository;
|
|
private readonly SettingsRepository _settingsRepository;
|
|
private readonly ApplicationDbContextFactory _dbContextFactory;
|
|
|
|
public DbMigrationsHostedService(InvoiceRepository invoiceRepository, SettingsRepository settingsRepository, ApplicationDbContextFactory dbContextFactory)
|
|
{
|
|
_invoiceRepository = invoiceRepository;
|
|
_settingsRepository = settingsRepository;
|
|
_dbContextFactory = dbContextFactory;
|
|
}
|
|
|
|
|
|
internal override Task[] InitializeTasks()
|
|
{
|
|
return new Task[] { ProcessMigration() };
|
|
}
|
|
|
|
protected async Task ProcessMigration()
|
|
{
|
|
var settings = await _settingsRepository.GetSettingAsync<MigrationSettings>();
|
|
if (settings.MigratedInvoiceTextSearchPages != int.MaxValue)
|
|
{
|
|
await MigratedInvoiceTextSearchToDb(settings.MigratedInvoiceTextSearchPages.Value);
|
|
}
|
|
|
|
// Refresh settings since these operations may run for very long time
|
|
}
|
|
|
|
private async Task MigratedInvoiceTextSearchToDb(int startFromPage)
|
|
{
|
|
using var ctx = _dbContextFactory.CreateContext();
|
|
|
|
var invoiceQuery = new InvoiceQuery { IncludeArchived = true };
|
|
var totalCount = await _invoiceRepository.GetInvoicesTotal(invoiceQuery);
|
|
const int PAGE_SIZE = 1000;
|
|
var totalPages = Math.Ceiling(totalCount * 1.0m / PAGE_SIZE);
|
|
for (int i = startFromPage; i < totalPages; i++)
|
|
{
|
|
invoiceQuery.Skip = i * PAGE_SIZE;
|
|
invoiceQuery.Take = PAGE_SIZE;
|
|
var invoices = await _invoiceRepository.GetInvoices(invoiceQuery);
|
|
|
|
foreach (var invoice in invoices)
|
|
{
|
|
var textSearch = new List<string>();
|
|
|
|
// recreating different textSearch.Adds that were previously in DBriize
|
|
foreach (var paymentMethod in invoice.GetPaymentMethods())
|
|
{
|
|
if (paymentMethod.Network != null)
|
|
{
|
|
var paymentDestination = paymentMethod.GetPaymentMethodDetails().GetPaymentDestination();
|
|
textSearch.Add(paymentDestination);
|
|
textSearch.Add(paymentMethod.Calculate().TotalDue.ToString());
|
|
}
|
|
}
|
|
//
|
|
textSearch.Add(invoice.Id);
|
|
textSearch.Add(invoice.InvoiceTime.ToString(CultureInfo.InvariantCulture));
|
|
textSearch.Add(invoice.Price.ToString(CultureInfo.InvariantCulture));
|
|
textSearch.Add(invoice.Metadata.OrderId);
|
|
textSearch.Add(InvoiceRepository.ToJsonString(invoice.Metadata, null));
|
|
textSearch.Add(invoice.StoreId);
|
|
textSearch.Add(invoice.Metadata.BuyerEmail);
|
|
//
|
|
textSearch.Add(invoice.RefundMail);
|
|
// TODO: Are there more things to cache? PaymentData?
|
|
|
|
InvoiceRepository.AddToTextSearch(ctx,
|
|
new InvoiceData { Id = invoice.Id, InvoiceSearchData = new List<InvoiceSearchData>() },
|
|
textSearch.ToArray());
|
|
}
|
|
|
|
var settings = await _settingsRepository.GetSettingAsync<MigrationSettings>();
|
|
if (i + 1 < totalPages)
|
|
{
|
|
settings.MigratedInvoiceTextSearchPages = i;
|
|
}
|
|
else
|
|
{
|
|
// during final pass we set int.MaxValue so migration doesn't run again
|
|
settings.MigratedInvoiceTextSearchPages = int.MaxValue;
|
|
}
|
|
|
|
// this call triggers update; we're sure that MigrationSettings is already initialized in db
|
|
// because of logic executed in MigrationStartupTask.cs
|
|
_settingsRepository.UpdateSettingInContext(ctx, settings);
|
|
await ctx.SaveChangesAsync();
|
|
}
|
|
}
|
|
}
|
|
}
|