private void ThrowIfInvalidToken(BatchAggregatorToken <TBatchItem> token)
 {
     if (token.Disposed)
     {
         throw new ObjectDisposedException("token", "The aggregator token was already disposed");
     }
     if (!_currentBatchAggregators.Contains(token))
     {
         throw new InvalidOperationException("The aggregator is not part of the current batch");
     }
 }
Example #2
0
 public bool Equals(BatchAggregatorToken <TBatchItem> other)
 {
     if (ReferenceEquals(null, other))
     {
         return(false);
     }
     if (ReferenceEquals(this, other))
     {
         return(true);
     }
     return(Id.Equals(other.Id));
 }
        /// <summary>
        /// Several concurrent callers can contribute to the batch each one of them must hold a token
        /// </summary>
        /// <returns></returns>
        public async Task <BatchAggregatorToken <TBatchItem> > NewBatchAggregatorToken()
        {
            var token = new BatchAggregatorToken <TBatchItem>(this);

            if (!await _allowItemsEvent.WaitAsync(_cts.Token))
            {
                throw new TaskCanceledException();
            }
            lock (_syncLock)
            {
                _currentBatchAggregators.Add(token);
            }

            _lastOperation = Guid.NewGuid();
            return(token);
        }
        public async Task Add(TBatchItem item, BatchAggregatorToken <TBatchItem> token, bool willAddMoreItemsWithThisToken = true)
        {
            ThrowIfInvalidToken(token);
            if (!await _allowItemsEvent.WaitAsync(_cts.Token))
            {
                throw new TaskCanceledException();
            }

            _items.Add(item);
            _lastOperation = Guid.NewGuid();
            if (!willAddMoreItemsWithThisToken)
            {
                await CompleteChunk(token, false);
            }
            else if (ChunkSize > 0 && _items.Count >= ChunkSize)
            {
                await CompleteChunk(token, true);
            }
        }
        private async Task CompleteChunk(BatchAggregatorToken <TBatchItem> token, bool chunkLimitReachedTriggers)
        {
            await AwaitEnlistersUntilNoMoreEnlist();

            Task awaitForTask;
            var  itemsToProcess = new TBatchItem[0];

            lock (_syncLock)
            {
                ThrowIfInvalidToken(token);

                if (!chunkLimitReachedTriggers)
                {
                    _currentBatchAggregators.Remove(token);
                }

                var executeBatch = chunkLimitReachedTriggers || !_currentBatchAggregators.Any();
                if (executeBatch)
                {
                    _allowItemsEvent.Reset();
                    Status = BatchStatus.Executing;
                    var batchItems = _items.ToArray();

                    var itemsToKeep = new TBatchItem[0];
                    if (ChunkSize == 0)
                    {
                        itemsToProcess = batchItems;
                    }
                    else
                    {
                        itemsToProcess = batchItems.Take(ChunkSize).ToArray();
                        itemsToKeep    = batchItems.Skip(ChunkSize).ToArray();
                    }

                    awaitForTask = RunChunk();
                    //immediately allow new additions
                    _items = new ConcurrentBag <TBatchItem>(itemsToKeep);
                    Status = BatchStatus.Opened;
                    _allowItemsEvent.Set();
                    _resetEvent.Set();
                    _resetEvent.Reset();
                }
                else
                {
                    awaitForTask = _resetEvent.WaitAsync(_cts.Token);
                }
            }

            await awaitForTask;

            async Task AwaitEnlistersUntilNoMoreEnlist()
            {
                var current = _lastOperation;
                //by doing this we are letting other asynchronous tasks to enlist before executing the batch
                await Task.Delay(EnlistAwaitTimeout);

                while (current != _lastOperation)
                {
                    current = _lastOperation;
                    await Task.Delay(EnlistAwaitTimeout);
                }
            }

            Task RunChunk()
            {
                try
                {
                    return(_batchChunkProcessor.Process(itemsToProcess, _cts.Token));
                }
                catch (Exception ex)
                {
                    ErrorResult errorResult;
                    int         attempts = 0;
                    do
                    {
                        errorResult = _batchChunkProcessor.HandleError(itemsToProcess, ex, ++attempts);
                    } while (errorResult == ErrorResult.Retry);

                    if (errorResult == ErrorResult.AbortAndRethrow)
                    {
                        throw;
                    }
                    return(Task.CompletedTask);

                    ;
                }
            }
        }
 public async Task AddingItemsToBatchCompleted(BatchAggregatorToken <TBatchItem> token)
 {
     await CompleteChunk(token, false);
 }