private async ValueTask HandleEvents(IReadOnlyList <ExBalanceUpdateMessage> messages) { using var activity = MyTelemetry.StartActivity("Handle ExBalanceUpdateMessage's") ?.AddTag("count-events", messages.Count); try { var sw = new Stopwatch(); sw.Start(); var list = new List <AvgPriceUpdate>(); var calculatedList = new List <AvgPriceUpdate>(); if (!messages.Any()) { return; } list.AddRange(GetOperationUpdates(messages)); if (!list.Any()) { return; } await using var ctx = DatabaseContext.Create(_dbContextOptionsBuilder); var groups = list.GroupBy(t => new { t.AssetId, t.WalletId }); foreach (var group in groups) { var semaphore = SemaphoreManager.GetOrCreate(group.Key.WalletId, group.Key.AssetId); await semaphore.WaitAsync(); try { var groupedList = group.OrderBy(t => t.TimeStamp).ToList(); var snapshot = await _snapshotManager.GetSnapshot(group.Key.WalletId, group.Key.AssetId); if (snapshot != null) { groupedList = groupedList.Where(t => t.TimeStamp > snapshot.LastTimestamp).ToList(); } if (!groupedList.Any()) { continue; } groupedList = AvgPriceCalculator.CalculateAvgPrice(groupedList, snapshot); calculatedList.AddRange(groupedList); var openTimestamp = groupedList.LastOrDefault(t => t.Balance == 0)?.TimeStamp ?? snapshot?.OpenTimestamp ?? default; var lastUpdate = groupedList.Last(); var newSnapshot = new SnapshotEntity { WalletId = lastUpdate.WalletId, AssetId = lastUpdate.AssetId, Balance = lastUpdate.Balance, AvgOpenPrice = lastUpdate.AvgOpenPrice, OpenTimestamp = openTimestamp, LastTimestamp = lastUpdate.TimeStamp }; await _snapshotManager.UpsertSnapshots(new() { newSnapshot }); } finally { semaphore.Release(); } } await ctx.UpsertAvgPriceAsync(calculatedList.Where(t => t.EventType != "Refund Blockchain Withdrawal").Select(update => new OperationHistoryEntity(update))); await ctx.SaveChangesAsync(); sw.Stop(); _logger.LogDebug( "Write {countUpdates} avg price updates in database from {countEvents} ME events. Time: {elapsedText} [{elapsedMilliseconds} ms]", list.Count, messages.Count, sw.Elapsed.ToString(), sw.ElapsedMilliseconds); } catch (Exception ex) { ex.FailActivity(); messages.AddToActivityAsJsonTag("me events"); throw; } }
private async ValueTask HandleEvents(IReadOnlyList <ExBalanceUpdateMessage> messages) { await Task.Delay(5000); using var locker = await _locker.GetLocker(); using var activity = MyTelemetry.StartActivity("Handle ExBalanceUpdateMessage's") ?.AddTag("count-events", messages.Count); try { var sw = new Stopwatch(); sw.Start(); var list = new List <BalanceHistoryEntity>(); foreach (var balanceUpdateMessage in messages) { if (balanceUpdateMessage.Update.Result != ExBalanceUpdate.BalanceUpdateResult.Ok) { continue; } var groupedUpdates = balanceUpdateMessage.Update.Updates.GroupBy(t => new { t.WalletId, t.AssetId }); foreach (var update in groupedUpdates) { using var _ = MyTelemetry.StartActivity("Update balance") ?.AddTag("walletId", update.Key.WalletId) .AddTag("symbol", update.Key.AssetId); var firstUpdate = update.OrderBy(t => t.Number).First(); var lastUpdate = update.OrderBy(t => t.Number).Last(); var newBalance = lastUpdate.NewBalance; var oldBalance = firstUpdate.OldBalance; var newReserve = lastUpdate.ReserveNewBalance; var oldReserve = firstUpdate.ReserveOldBalance; newBalance += newReserve; oldBalance += oldReserve; var amountBalance = newBalance - oldBalance; var amountReserve = newReserve - oldReserve; var availableBalance = newBalance - newReserve; var balanceVersion = balanceUpdateMessage.Update.Balances.Single(balance => balance.AssetId == update.Key.AssetId && balance.WalletId == update.Key.WalletId).Version; var entity = new BalanceHistoryEntity() { WalletId = update.Key.WalletId, Symbol = update.Key.AssetId, EventType = balanceUpdateMessage.Update.EventType, SequenceId = balanceVersion, OperationId = balanceUpdateMessage.Update.OperationId, Timestamp = balanceUpdateMessage.Update.Timestamp, NewBalance = (double)newBalance, OldBalance = (double)oldBalance, NewReserve = (double)newReserve, OldReserve = (double)oldReserve, AmountBalance = (double)amountBalance, AmountReserve = (double)amountReserve, AvailableBalance = (double)availableBalance, IsBalanceChanged = amountBalance != 0 }; list.Add(entity); } } await using var ctx = DatabaseContext.Create(_dbContextOptionsBuilder); await ctx.UpsetAsync(list).ConfigureAwait(false); await PopulateBalances(list, ctx); sw.Stop(); _logger.LogDebug( "Write {countUpdates} balance updates in database from {countEvents} ExBalanceUpdateMessage events. Time: {elapsedText} [{elapsedMilliseconds} ms]", list.Count, messages.Count, sw.Elapsed.ToString(), sw.ElapsedMilliseconds); } catch (Exception ex) { ex.FailActivity(); messages.AddToActivityAsJsonTag("me events"); throw; } }