private async Task ProcessBatchesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { _logger.LogInformation($"Sleeping for {_configuration.BatchFrequencyInMilliseconds} ms.".AddTimestamp()); await Task.Delay(_configuration.BatchFrequencyInMilliseconds, cancellationToken); List <Bet> batch; ulong deliveryTag; lock (_betsBatch) { batch = _betsBatch; _betsBatch = new List <Bet>(); deliveryTag = _lastDeliveryTag; } if (!batch.Any()) { _logger.LogInformation("No batch to process.".AddTimestamp()); continue; } _logger.LogInformation($"Processing batch of {batch.Count} bets.".AddTimestamp()); var stakeIds = batch.Select(b => b.StakeId); var betsByStakeIds = batch.GroupBy(b => b.StakeId).ToDictionary(g => g.Key, g => g.Count()); using (var dbContext = new BetsDbContext(_configuration.ConnectionString)) using (var transaction = await dbContext.Database.BeginTransactionAsync(IsolationLevel.RepeatableRead, cancellationToken)) { var matches = await dbContext.Matches.Include(m => m.Stakes).Where(m => m.Stakes.Any(s => stakeIds.Contains(s.Id))) .ToListAsync(cancellationToken); _logger.LogInformation($"Got {matches.Count} matches".AddTimestamp()); var newStakes = matches.SelectMany(m => m.Stakes.Where(s => s.IsBettable).Select(s => CalculateNewStake(s, m, betsByStakeIds))).ToList(); foreach (var oldStake in matches.SelectMany(m => m.Stakes.Where(s => s.IsBettable))) { oldStake.IsBettable = false; } _logger.LogInformation("Calculated stakes."); dbContext.Stakes.AddRange(newStakes); await dbContext.SaveChangesAsync(cancellationToken); transaction.Commit(); } EventConsumer.Ack(deliveryTag); } }