Use raw SQL INSERT IGNORE for TradeRepository.AddRangeAsync to prevent DbUpdateException on duplicates
This commit is contained in:
@@ -48,7 +48,10 @@ public class TradeRepository : ITradeRepository
|
|||||||
|
|
||||||
public async Task AddRangeAsync(IEnumerable<Trade> trades, CancellationToken ct = default)
|
public async Task AddRangeAsync(IEnumerable<Trade> trades, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
foreach (var t in trades)
|
var tradeList = trades.ToList();
|
||||||
|
if (tradeList.Count == 0) return;
|
||||||
|
|
||||||
|
foreach (var t in tradeList)
|
||||||
{
|
{
|
||||||
t.Outcome = StringHelper.Truncate(t.Outcome, 128) ?? "";
|
t.Outcome = StringHelper.Truncate(t.Outcome, 128) ?? "";
|
||||||
t.PlatformTradeId = StringHelper.Truncate(t.PlatformTradeId, 256) ?? "";
|
t.PlatformTradeId = StringHelper.Truncate(t.PlatformTradeId, 256) ?? "";
|
||||||
@@ -58,31 +61,38 @@ public class TradeRepository : ITradeRepository
|
|||||||
t.TransactionHash = StringHelper.Truncate(t.TransactionHash, 66);
|
t.TransactionHash = StringHelper.Truncate(t.TransactionHash, 66);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
foreach (var chunk in tradeList.Chunk(1000))
|
||||||
{
|
{
|
||||||
_db.Trades.AddRange(trades);
|
var sb = new System.Text.StringBuilder("INSERT IGNORE INTO Trades (PlatformTradeId, MarketId, AssetId, Outcome, Side, Price, Size, Amount, ExecutedAt, TransactionHash, TraderId, MarketOutcomeId, DbMarketId, Platform, IsContextEnriched) VALUES ");
|
||||||
await _db.SaveChangesAsync(ct);
|
var parameters = new List<object>();
|
||||||
}
|
|
||||||
catch
|
for (int i = 0; i < chunk.Length; i++)
|
||||||
{
|
{
|
||||||
foreach (var t in trades)
|
var t = chunk[i];
|
||||||
{
|
int pIdx = i * 15;
|
||||||
try { _db.Entry(t).State = EntityState.Detached; } catch { }
|
sb.Append($"({{{pIdx}}}, {{{pIdx + 1}}}, {{{pIdx + 2}}}, {{{pIdx + 3}}}, {{{pIdx + 4}}}, {{{pIdx + 5}}}, {{{pIdx + 6}}}, {{{pIdx + 7}}}, {{{pIdx + 8}}}, {{{pIdx + 9}}}, {{{pIdx + 10}}}, {{{pIdx + 11}}}, {{{pIdx + 12}}}, {{{pIdx + 13}}}, {{{pIdx + 14}}})");
|
||||||
|
|
||||||
|
if (i < chunk.Length - 1)
|
||||||
|
sb.Append(", ");
|
||||||
|
|
||||||
|
parameters.Add(t.PlatformTradeId);
|
||||||
|
parameters.Add(t.MarketId);
|
||||||
|
parameters.Add(t.AssetId);
|
||||||
|
parameters.Add(t.Outcome);
|
||||||
|
parameters.Add((int)t.Side);
|
||||||
|
parameters.Add(t.Price);
|
||||||
|
parameters.Add(t.Size);
|
||||||
|
parameters.Add(t.Amount);
|
||||||
|
parameters.Add(t.ExecutedAt);
|
||||||
|
parameters.Add(t.TransactionHash ?? (object)DBNull.Value);
|
||||||
|
parameters.Add(t.TraderId);
|
||||||
|
parameters.Add(t.MarketOutcomeId ?? (object)DBNull.Value);
|
||||||
|
parameters.Add(t.DbMarketId ?? (object)DBNull.Value);
|
||||||
|
parameters.Add((int)t.Platform);
|
||||||
|
parameters.Add(t.IsContextEnriched);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to inserting one by one to avoid losing the entire batch due to a single duplicate
|
await _db.Database.ExecuteSqlRawAsync(sb.ToString(), parameters.ToArray(), ct);
|
||||||
foreach (var t in trades)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_db.Trades.Add(t);
|
|
||||||
await _db.SaveChangesAsync(ct);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
try { _db.Entry(t).State = EntityState.Detached; } catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user