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); } }
public async Task <IActionResult> PutAsync(Bet bet) { _logger.LogInformation($"Bet:\n{JsonConvert.SerializeObject(bet)}"); var subject = User.GetSubjectClaim(); if (bet.UserId != subject) { _logger.LogError($"Unauthorized claim {subject} for userId = {bet.UserId}."); return(Unauthorized()); } var stake = await _betsDbContext.Stakes .Include(s => s.Match) .SingleOrDefaultAsync(s => s.Id == bet.StakeId); if (stake == null) { return(BadRequest($"Specified stake ${bet.StakeId} does not exist.")); } if (stake.Match.IsFinished) { return(BadRequest($"Match ${stake.MatchId} already finished.")); } var account = await _betsDbContext.Accounts.SingleOrDefaultAsync(a => a.UserId == bet.UserId); if (account != null && account.Balance < bet.Amount) { return(BadRequest($"Insufficient funds.")); } _betsDbContext.Bets.Add(bet); _betEventProducer.PublishNewBet(bet); await _betsDbContext.SaveChangesAsync(); return(Ok()); }
public async Task Save() { await _context.SaveChangesAsync(); }