Example #1
0
        private Task ConsumeTopicPartitionAsync(string topic, int partitionId)
        {
            return Task.Run(async () =>
            {
                try
                {
                    bool refreshMetaData = false;
                    var bufferSizeHighWatermark = FetchRequest.DefaultBufferSize;

                    _options.Log.DebugFormat("Consumer: Creating polling task for topic: {0} on parition: {1}", topic, partitionId);
                    while (_disposeToken.IsCancellationRequested == false)
                    {
                        if (refreshMetaData)
                        {
                            await _options.Router.RefreshTopicMetadata(topic);
                            EnsurePartitionPollingThreads();
                            refreshMetaData = false;
                        }

                        try
                        {
                            //get the current offset, or default to zero if not there.
                            long offset = 0;
                            _partitionOffsetIndex.AddOrUpdate(partitionId, i => offset, (i, currentOffset) =>
                            {
                                offset = currentOffset;
                                return currentOffset;
                            });

                            //build a fetch request for partition at offset
                            var fetch = new Fetch
                            {
                                Topic = topic,
                                PartitionId = partitionId,
                                Offset = offset,
                                MaxBytes = bufferSizeHighWatermark,
                            };

                            var fetchRequest = fetch.ToFetchRequest(_options);

                            //make request and post to queue
                            var route = _options.Router.SelectBrokerRouteFromLocalCache(topic, partitionId);

                            var taskSend = route.Connection.SendAsync(fetchRequest);

                            await Task.WhenAny(taskSend, _disposeTask.Task).ConfigureAwait(false);
                            if (_disposeTask.Task.IsCompleted) return;

                            var responses = await taskSend; //already done
                            if (responses.Count > 0)
                            {
                                var response = responses.FirstOrDefault(); //we only asked for one response
                                
                                var hasProcessed = await ProcessResponseAndUpdateOffsetAsync(response, fetch, partitionId);

                                if (_disposeToken.IsCancellationRequested) return;

                                // sleep is not needed if responses were received
                                if (hasProcessed) continue;
                            }

                            //no message received from server wait a while before we try another long poll
                            await Task.Delay(_options.BackoffInterval, _disposeToken.Token);
                        }
                        catch (BufferUnderRunException ex)
                        {
                            bufferSizeHighWatermark = (int)(ex.RequiredBufferSize * _options.FetchBufferMultiplier) +
                                                      ex.MessageHeaderSize;
                            _options.Log.InfoFormat("Buffer underrun.  Increasing buffer size to: {0}",
                                bufferSizeHighWatermark);
                        }
                        catch (OffsetOutOfRangeException ex)
                        {
                            //TODO this turned out really ugly.  Need to fix this section.
                            _options.Log.ErrorFormat(ex.Message);
                            FixOffsetOutOfRangeExceptionAsync(ex.FetchRequest);
                        }
                        catch (InvalidMetadataException ex)
                        {
                            //refresh our metadata and ensure we are polling the correct partitions
                            refreshMetaData = true;
                            _options.Log.ErrorFormat(ex.Message);
                        }
                        catch (TaskCanceledException ex)
                        {
                            //TODO :LOG
                        }
                        catch (Exception ex)
                        {
                            _options.Log.ErrorFormat("Exception occured while polling topic:{0} partition:{1}.  Polling will continue.  Exception={2}", topic, partitionId, ex);
                        }
                    }
                }
                finally
                {
                    _options.Log.DebugFormat("Consumer: Disabling polling task for topic: {0} on parition: {1}", topic, partitionId);
                    Task tempTask;
                    _partitionPollingIndex.TryRemove(partitionId, out tempTask);
                }
            });
        }