Better handle postgres requests for wallet objects (#4985)

This commit is contained in:
Nicolas Dorier 2023-05-20 23:26:16 +09:00 committed by GitHub
parent c3f412e3bb
commit 9b721fae27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -7,10 +7,12 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using Dapper;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NBitcoin; using NBitcoin;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Npgsql;
namespace BTCPayServer.Services namespace BTCPayServer.Services
{ {
@ -365,6 +367,11 @@ namespace BTCPayServer.Services
{ {
SortWalletObjectLinks(ref a, ref b); SortWalletObjectLinks(ref a, ref b);
await using var ctx = _ContextFactory.CreateContext(); await using var ctx = _ContextFactory.CreateContext();
await UpdateWalletObjectLink(a, b, data, ctx, true);
}
private static async Task UpdateWalletObjectLink(WalletObjectId a, WalletObjectId b, JObject? data, ApplicationDbContext ctx, bool doNothingIfExists)
{
var l = new WalletObjectLinkData() var l = new WalletObjectLinkData()
{ {
WalletId = a.WalletId.ToString(), WalletId = a.WalletId.ToString(),
@ -374,13 +381,34 @@ namespace BTCPayServer.Services
BId = b.Id, BId = b.Id,
Data = data?.ToString(Formatting.None) Data = data?.ToString(Formatting.None)
}; };
ctx.WalletObjectLinks.Add(l); if (!ctx.Database.IsNpgsql())
{
var e = ctx.WalletObjectLinks.Add(l);
try try
{ {
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
catch (DbUpdateException) // already exists catch (DbUpdateException) // already exists
{ {
if (!doNothingIfExists)
{
e.State = EntityState.Modified;
await ctx.SaveChangesAsync();
}
}
}
else
{
var connection = ctx.Database.GetDbConnection();
var conflict = doNothingIfExists ? "ON CONFLICT DO NOTHING" : "ON CONFLICT ON CONSTRAINT \"PK_WalletObjectLinks\" DO UPDATE SET \"Data\"=EXCLUDED.\"Data\"";
try
{
await connection.ExecuteAsync("INSERT INTO \"WalletObjectLinks\" VALUES (@WalletId, @AType, @AId, @BType, @BId, @Data::JSONB) " + conflict, l);
}
catch (PostgresException ex) when (ex.SqlState == PostgresErrorCodes.ForeignKeyViolation)
{
throw new DbUpdateException();
}
} }
} }
@ -411,25 +439,7 @@ namespace BTCPayServer.Services
await using var ctx = _ContextFactory.CreateContext(); await using var ctx = _ContextFactory.CreateContext();
var l = new WalletObjectLinkData() await UpdateWalletObjectLink(a, b, data, ctx, false);
{
WalletId = a.WalletId.ToString(),
AType = a.Type,
AId = a.Id,
BType = b.Type,
BId = b.Id,
Data = data?.ToString(Formatting.None)
};
var e = ctx.WalletObjectLinks.Add(l);
try
{
await ctx.SaveChangesAsync();
}
catch (DbUpdateException) // already exists
{
e.State = EntityState.Modified;
await ctx.SaveChangesAsync();
}
} }
public static int MaxCommentSize = 200; public static int MaxCommentSize = 200;
@ -558,24 +568,35 @@ namespace BTCPayServer.Services
{ {
ArgumentNullException.ThrowIfNull(id); ArgumentNullException.ThrowIfNull(id);
await using var ctx = _ContextFactory.CreateContext(); await using var ctx = _ContextFactory.CreateContext();
var o = NewWalletObjectData(id, data); var wo = NewWalletObjectData(id, data);
ctx.WalletObjects.Add(o); if (!ctx.Database.IsNpgsql())
{
ctx.WalletObjects.Add(wo);
try try
{ {
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
catch (DbUpdateException) // already exists catch (DbUpdateException) // already exists
{ {
ctx.Entry(o).State = EntityState.Modified; ctx.Entry(wo).State = EntityState.Modified;
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
} }
else
{
var connection = ctx.Database.GetDbConnection();
await connection.ExecuteAsync("INSERT INTO \"WalletObjects\" VALUES (@WalletId, @Type, @Id, @Data::JSONB) ON CONFLICT ON CONSTRAINT \"PK_WalletObjects\" DO UPDATE SET \"Data\"=EXCLUDED.\"Data\"", wo);
}
}
public async Task EnsureWalletObject(WalletObjectId id, JObject? data = null) public async Task EnsureWalletObject(WalletObjectId id, JObject? data = null)
{ {
ArgumentNullException.ThrowIfNull(id); ArgumentNullException.ThrowIfNull(id);
var wo = NewWalletObjectData(id, data);
await using var ctx = _ContextFactory.CreateContext(); await using var ctx = _ContextFactory.CreateContext();
ctx.WalletObjects.Add(NewWalletObjectData(id, data)); if (!ctx.Database.IsNpgsql())
{
ctx.WalletObjects.Add(wo);
try try
{ {
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
@ -584,6 +605,12 @@ namespace BTCPayServer.Services
{ {
} }
} }
else
{
var connection = ctx.Database.GetDbConnection();
await connection.ExecuteAsync("INSERT INTO \"WalletObjects\" VALUES (@WalletId, @Type, @Id, @Data::JSONB) ON CONFLICT DO NOTHING", wo);
}
}
#nullable restore #nullable restore
} }
} }