示例#1
0
        /// <summary>
        ///   Receives a batch of <see cref="EventData" /> from the Event Hub partition.
        /// </summary>
        ///
        /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param>
        /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the default wait time specified when the consumer was created will be used.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>The batch of <see cref="EventData" /> from the Event Hub partition this consumer is associated with.  If no events are present, an empty enumerable is returned.</returns>
        ///
        public override async Task <IEnumerable <EventData> > ReceiveAsync(int maximumMessageCount,
                                                                           TimeSpan?maximumWaitTime,
                                                                           CancellationToken cancellationToken)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpEventHubConsumer));
            Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount));

            var receivedEventCount = 0;
            var failedAttemptCount = 0;
            var waitTime           = (maximumWaitTime ?? _tryTimeout);
            var link           = default(ReceivingAmqpLink);
            var retryDelay     = default(TimeSpan?);
            var amqpMessages   = default(IEnumerable <AmqpMessage>);
            var receivedEvents = default(List <EventData>);

            var stopWatch = Stopwatch.StartNew();

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        EventHubsEventSource.Log.EventReceiveStart(EventHubName, ConsumerGroup, PartitionId);

                        link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, _tryTimeout)).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        var messagesReceived = await Task.Factory.FromAsync
                                               (
                            (callback, state) => link.BeginReceiveMessages(maximumMessageCount, waitTime, callback, state),
                            (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages),
                            TaskCreationOptions.RunContinuationsAsynchronously
                                               ).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        // If event messages were received, then package them for consumption and
                        // return them.

                        if ((messagesReceived) && (amqpMessages != null))
                        {
                            receivedEvents ??= new List <EventData>();

                            foreach (AmqpMessage message in amqpMessages)
                            {
                                link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome);
                                receivedEvents.Add(MessageConverter.CreateEventFromMessage(message));
                                message.Dispose();
                            }

                            receivedEventCount = receivedEvents.Count;

                            if ((Options.TrackLastEnqueuedEventInformation) && (receivedEventCount > 0))
                            {
                                LastReceivedEvent = receivedEvents[receivedEventCount - 1];
                            }

                            return(receivedEvents);
                        }

                        // No events were available.

                        return(Enumerable.Empty <EventData>());
                    }
                    catch (EventHubsTimeoutException)
                    {
                        // Because the timeout specified with the request is intended to be the maximum
                        // amount of time to wait for events, a timeout isn't considered an error condition,
                        // rather a sign that no events were available in the requested period.

                        return(Enumerable.Empty <EventData>());
                    }
                    catch (AmqpException amqpException)
                    {
                        throw AmqpError.CreateExceptionForError(amqpException.Error, EventHubName);
                    }
                    catch (Exception ex)
                    {
                        // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop.
                        // Otherwise, bubble the exception.

                        ++failedAttemptCount;
                        retryDelay = _retryPolicy.CalculateRetryDelay(ex, failedAttemptCount);

                        if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested))
                        {
                            EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, ex.Message);
                            await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.Elapsed)), cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                // If no value has been returned nor exception thrown by this point,
                // then cancellation has been requested.

                throw new TaskCanceledException();
            }
            catch (Exception ex)
            {
                EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, ex.Message);
                throw;
            }
            finally
            {
                stopWatch.Stop();
                EventHubsEventSource.Log.EventReceiveComplete(EventHubName, ConsumerGroup, PartitionId, receivedEventCount);
            }
        }
示例#2
0
        /// <summary>
        ///   Receives a batch of <see cref="EventData" /> from the Event Hub partition.
        /// </summary>
        ///
        /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param>
        /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the per-try timeout specified by the retry policy will be used.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>The batch of <see cref="EventData" /> from the Event Hub partition this consumer is associated with.  If no events are present, an empty set is returned.</returns>
        ///
        public override async Task <IReadOnlyList <EventData> > ReceiveAsync(int maximumMessageCount,
                                                                             TimeSpan?maximumWaitTime,
                                                                             CancellationToken cancellationToken)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpConsumer));
            Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount));

            var receivedEventCount = 0;
            var failedAttemptCount = 0;
            var tryTimeout         = RetryPolicy.CalculateTryTimeout(0);
            var waitTime           = (maximumWaitTime ?? tryTimeout);
            var link              = default(ReceivingAmqpLink);
            var retryDelay        = default(TimeSpan?);
            var amqpMessages      = default(IEnumerable <AmqpMessage>);
            var receivedEvents    = default(List <EventData>);
            var lastReceivedEvent = default(EventData);

            var stopWatch = ValueStopwatch.StartNew();

            try
            {
                while ((!cancellationToken.IsCancellationRequested) && (!_closed))
                {
                    try
                    {
                        // Creation of the link happens without explicit knowledge of the cancellation token
                        // used for this operation; validate the token state before attempting link creation and
                        // again after the operation completes to provide best efforts in respecting it.

                        EventHubsEventSource.Log.EventReceiveStart(EventHubName, ConsumerGroup, PartitionId);
                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout)).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        var messagesReceived = await Task.Factory.FromAsync
                                               (
                            (callback, state) => link.BeginReceiveMessages(maximumMessageCount, waitTime, callback, state),
                            (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages),
                            TaskCreationOptions.RunContinuationsAsynchronously
                                               ).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        // If event messages were received, then package them for consumption and
                        // return them.

                        if ((messagesReceived) && (amqpMessages != null))
                        {
                            receivedEvents ??= new List <EventData>();

                            foreach (AmqpMessage message in amqpMessages)
                            {
                                link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome);
                                receivedEvents.Add(MessageConverter.CreateEventFromMessage(message));
                                message.Dispose();
                            }

                            receivedEventCount = receivedEvents.Count;

                            if (receivedEventCount > 0)
                            {
                                lastReceivedEvent = receivedEvents[receivedEventCount - 1];

                                if (lastReceivedEvent.Offset > long.MinValue)
                                {
                                    CurrentEventPosition = EventPosition.FromOffset(lastReceivedEvent.Offset, false);
                                }

                                if (TrackLastEnqueuedEventProperties)
                                {
                                    LastReceivedEvent = lastReceivedEvent;
                                }
                            }

                            return(receivedEvents);
                        }

                        // No events were available.

                        return(EmptyEventSet);
                    }
                    catch (EventHubsException ex) when(ex.Reason == EventHubsException.FailureReason.ServiceTimeout)
                    {
                        // Because the timeout specified with the request is intended to be the maximum
                        // amount of time to wait for events, a timeout isn't considered an error condition,
                        // rather a sign that no events were available in the requested period.

                        return(EmptyEventSet);
                    }
                    catch (Exception ex)
                    {
                        Exception activeEx = ex.TranslateServiceException(EventHubName);

                        // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop.
                        // Otherwise, bubble the exception.

                        ++failedAttemptCount;
                        retryDelay = RetryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount);

                        if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested))
                        {
                            EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, activeEx.Message);
                            await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.GetElapsedTime())), cancellationToken).ConfigureAwait(false);

                            tryTimeout = RetryPolicy.CalculateTryTimeout(failedAttemptCount);
                        }
                        else if (ex is AmqpException)
                        {
                            ExceptionDispatchInfo.Capture(activeEx).Throw();
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                // If no value has been returned nor exception thrown by this point,
                // then cancellation has been requested.

                throw new TaskCanceledException();
            }
            catch (TaskCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, ex.Message);
                throw;
            }
            finally
            {
                EventHubsEventSource.Log.EventReceiveComplete(EventHubName, ConsumerGroup, PartitionId, receivedEventCount);
            }
        }
示例#3
0
        /// <summary>
        ///   Receives a batch of <see cref="EventData" /> from the Event Hub partition.
        /// </summary>
        ///
        /// <param name="maximumEventCount">The maximum number of messages to receive in this batch.</param>
        /// <param name="maximumWaitTime">The maximum amount of time to wait for events to become available, if no events can be read from the prefetch queue.  If not specified, the per-try timeout specified by the retry policy will be used.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>The batch of <see cref="EventData" /> from the Event Hub partition this consumer is associated with.  If no events are present, an empty set is returned.</returns>
        ///
        /// <remarks>
        ///   When events are available in the prefetch queue, they will be used to form the batch as quickly as possible without waiting for additional events from the
        ///   Event Hubs service to try and meet the requested <paramref name="maximumEventCount" />.  When no events are available in prefetch, the receiver will wait up
        ///   to the <paramref name="maximumWaitTime"/> for events to be read from the service.  Once any events are available, they will be used to form the batch immediately.
        /// </remarks>
        ///
        public override async Task <IReadOnlyList <EventData> > ReceiveAsync(int maximumEventCount,
                                                                             TimeSpan?maximumWaitTime,
                                                                             CancellationToken cancellationToken)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpConsumer));
            Argument.AssertNotClosed(ConnectionScope.IsDisposed, nameof(EventHubConnection));
            Argument.AssertAtLeast(maximumEventCount, 1, nameof(maximumEventCount));

            var receivedEventCount = 0;
            var failedAttemptCount = 0;
            var tryTimeout         = RetryPolicy.CalculateTryTimeout(0);
            var waitTime           = (maximumWaitTime ?? tryTimeout);
            var operationId        = Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture);
            var link              = default(ReceivingAmqpLink);
            var retryDelay        = default(TimeSpan?);
            var receivedEvents    = default(List <EventData>);
            var lastReceivedEvent = default(EventData);

            var stopWatch = ValueStopwatch.StartNew();

            try
            {
                while ((!cancellationToken.IsCancellationRequested) && (!_closed))
                {
                    try
                    {
                        // Creation of the link happens without explicit knowledge of the cancellation token
                        // used for this operation; validate the token state before attempting link creation and
                        // again after the operation completes to provide best efforts in respecting it.

                        EventHubsEventSource.Log.EventReceiveStart(EventHubName, ConsumerGroup, PartitionId, operationId);

                        link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout), cancellationToken).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        var messagesReceived = await link.ReceiveMessagesAsync(maximumEventCount, ReceiveBuildBatchInterval, waitTime, cancellationToken).ConfigureAwait(false);

                        // If no messages were received, then just return the empty set.

                        if (messagesReceived == null)
                        {
                            return(EmptyEventSet);
                        }

                        // If event messages were received, then package them for consumption and
                        // return them.

                        foreach (AmqpMessage message in messagesReceived)
                        {
                            receivedEvents ??= new List <EventData>();

                            link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome);
                            receivedEvents.Add(MessageConverter.CreateEventFromMessage(message));
                            message.Dispose();

                            receivedEventCount = receivedEvents.Count;
                        }

                        if (receivedEventCount > 0)
                        {
                            lastReceivedEvent = receivedEvents[receivedEventCount - 1];

                            if (lastReceivedEvent.Offset > long.MinValue)
                            {
                                CurrentEventPosition = EventPosition.FromOffset(lastReceivedEvent.Offset, false);
                            }

                            if (TrackLastEnqueuedEventProperties)
                            {
                                LastReceivedEvent = lastReceivedEvent;
                            }
                        }

                        return(receivedEvents ?? EmptyEventSet);
                    }
                    catch (EventHubsException ex) when(ex.Reason == EventHubsException.FailureReason.ServiceTimeout)
                    {
                        // Because the timeout specified with the request is intended to be the maximum
                        // amount of time to wait for events, a timeout isn't considered an error condition,
                        // rather a sign that no events were available in the requested period.

                        return(EmptyEventSet);
                    }
                    catch (Exception ex)
                    {
                        Exception activeEx = ex.TranslateServiceException(EventHubName);

                        // If the partition was stolen determine the correct action to take for
                        // capturing it with respect to whether the consumer should be invalidated.
                        //
                        // In either case, it is a terminal exception and will not trigger a retry;
                        // allow the normal error handling flow to surface the exception.

                        if (ex.IsConsumerPartitionStolenException())
                        {
                            // If the consumer should be invalidated, capture the exception
                            // and force-close the link.  This will ensure that the next operation
                            // will surface it.

                            if (InvalidateConsumerWhenPartitionStolen)
                            {
                                _activePartitionStolenException = ex;
                                CloseConsumerLink(link);
                            }
                            else
                            {
                                // If the consumer should not be invalidated, clear any previously captured exception to avoid
                                // surfacing the failure multiple times.  If the link is stolen after this operation, it will
                                // be intercepted and handled as needed.

                                _activePartitionStolenException = null;
                            }
                        }

                        // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop.
                        // Otherwise, bubble the exception.

                        ++failedAttemptCount;
                        retryDelay = RetryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount);

                        if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!_closed) && (!cancellationToken.IsCancellationRequested))
                        {
                            EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, operationId, activeEx.Message);
                            await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.GetElapsedTime())), cancellationToken).ConfigureAwait(false);

                            tryTimeout = RetryPolicy.CalculateTryTimeout(failedAttemptCount);
                        }
                        else if (ex is AmqpException)
                        {
                            ExceptionDispatchInfo.Capture(activeEx).Throw();
                        }
                        else
                        {
                            throw;
                        }
                    }
                }

                // If no value has been returned nor exception thrown by this point,
                // then cancellation has been requested.

                throw new TaskCanceledException();
            }
            catch (TaskCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                EventHubsEventSource.Log.EventReceiveError(EventHubName, ConsumerGroup, PartitionId, operationId, ex.Message);
                throw;
            }
            finally
            {
                EventHubsEventSource.Log.EventReceiveComplete(EventHubName, ConsumerGroup, PartitionId, operationId, failedAttemptCount, receivedEventCount);
            }
        }