示例#1
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="AmqpEventHubConsumer"/> class.
        /// </summary>
        ///
        /// <param name="eventHubName">The name of the Event Hub from which events will be consumed.</param>
        /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
        /// <param name="consumerOptions">The set of active options for the consumer that will make use of the link.</param>
        /// <param name="eventPosition">The position of the event in the partition where the consumer should begin reading.</param>
        /// <param name="connectionScope">The AMQP connection context for operations .</param>
        /// <param name="messageConverter">The converter to use for translating between AMQP messages and client types.</param>
        /// <param name="retryPolicy">The retry policy to consider when an operation fails.</param>
        ///
        /// <remarks>
        ///   As an internal type, this class performs only basic sanity checks against its arguments.  It
        ///   is assumed that callers are trusted and have performed deep validation.
        ///
        ///   Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose;
        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the
        ///   caller.
        /// </remarks>
        ///
        public AmqpEventHubConsumer(string eventHubName,
                                    string consumerGroup,
                                    string partitionId,
                                    EventPosition eventPosition,
                                    EventHubConsumerOptions consumerOptions,
                                    AmqpConnectionScope connectionScope,
                                    AmqpMessageConverter messageConverter,
                                    EventHubRetryPolicy retryPolicy)
        {
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNullOrEmpty(consumerGroup, nameof(consumerGroup));
            Argument.AssertNotNullOrEmpty(partitionId, nameof(partitionId));
            Argument.AssertNotNull(eventPosition, nameof(EventPosition));
            Argument.AssertNotNull(consumerOptions, nameof(EventHubConsumerOptions));
            Argument.AssertNotNull(connectionScope, nameof(connectionScope));
            Argument.AssertNotNull(messageConverter, nameof(messageConverter));
            Argument.AssertNotNull(retryPolicy, nameof(retryPolicy));

            EventHubName     = eventHubName;
            ConsumerGroup    = consumerGroup;
            PartitionId      = partitionId;
            Options          = consumerOptions;
            ConnectionScope  = connectionScope;
            MessageConverter = messageConverter;
            ReceiveLink      = new FaultTolerantAmqpObject <ReceivingAmqpLink>(timeout => ConnectionScope.OpenConsumerLinkAsync(consumerGroup, partitionId, eventPosition, consumerOptions, timeout, CancellationToken.None), link => link.SafeClose());

            _retryPolicy = retryPolicy;
            _tryTimeout  = retryPolicy.CalculateTryTimeout(0);
        }
        public void SettingTheRetryPolicyUpdatesState()
        {
            var retryOptions = new RetryOptions
            {
                Delay          = TimeSpan.FromSeconds(1),
                MaximumDelay   = TimeSpan.FromSeconds(2),
                TryTimeout     = TimeSpan.FromSeconds(3),
                MaximumRetries = 4,
                Mode           = RetryMode.Fixed
            };

            var customRetry     = Mock.Of <EventHubRetryPolicy>();
            var consumerOptions = new EventHubConsumerOptions {
                RetryOptions = retryOptions
            };
            var consumer = new EventHubConsumer(new ObservableTransportConsumerMock(), "dummy", "consumerGroup", "0", EventPosition.Latest, consumerOptions, new BasicRetryPolicy(retryOptions));

            Assert.That(consumer.RetryPolicy, Is.InstanceOf <BasicRetryPolicy>(), "The retry policy should have been created from options");

            consumer.RetryPolicy = customRetry;
            Assert.That(consumer.RetryPolicy, Is.SameAs(customRetry), "The custom retry policy should have been set.");

            var activeOptions = (EventHubConsumerOptions)
                                typeof(EventHubConsumer)
                                .GetProperty("Options", BindingFlags.Instance | BindingFlags.NonPublic)
                                .GetValue(consumer);

            Assert.That(activeOptions.RetryOptions, Is.Null, "Setting a custom policy should clear the retry options.");
        }
示例#3
0
        /// <summary>
        ///   Creates an Event Hub consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
        ///   and as a member of a specific consumer group.
        ///
        ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
        ///   group to ensure that only one consumer from that group is reading the from the partition.
        ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
        ///
        ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
        ///   group to be actively reading events from the partition.  These non-exclusive consumers are
        ///   sometimes referred to as "Non-epoch Consumers."
        ///
        ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
        ///   By default, consumers are created as non-exclusive.
        /// </summary>
        ///
        /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
        /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
        /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="consumerOptions" />.</param>
        ///
        /// <returns>An Event Hub consumer configured in the requested manner.</returns>
        ///
        public override EventHubConsumer CreateConsumer(string consumerGroup,
                                                        string partitionId,
                                                        EventPosition eventPosition,
                                                        EventHubConsumerOptions consumerOptions,
                                                        EventHubRetryPolicy defaultRetryPolicy)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpEventHubClient));

            LastEnqueuedEventProperties lastEnqueuedEventProperties = consumerOptions.TrackLastEnqueuedEventInformation
                ? new LastEnqueuedEventProperties(EventHubName, partitionId)
                : null;

            EventHubRetryPolicy retryPolicy = defaultRetryPolicy ?? _retryPolicy;

            var transportConsumer = new AmqpEventHubConsumer
                                    (
                EventHubName,
                consumerGroup,
                partitionId,
                eventPosition,
                consumerOptions,
                ConnectionScope,
                MessageConverter,
                retryPolicy,
                lastEnqueuedEventProperties
                                    );

            return(new EventHubConsumer(transportConsumer, EventHubName, consumerGroup, partitionId, eventPosition, consumerOptions, retryPolicy));
        }
示例#4
0
        /// <summary>
        ///   Opens an AMQP link for use with consumer operations.
        /// </summary>
        ///
        /// <param name="consumerGroup">The name of the consumer group in the context of which events should be received.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events should be received.</param>
        /// <param name="eventPosition">The position of the event in the partition where the link should be filtered to.</param>
        /// <param name="consumerOptions">The set of active options for the consumer that will make use of the link.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use with consumer operations.</returns>
        ///
        public virtual async Task <ReceivingAmqpLink> OpenConsumerLinkAsync(string consumerGroup,
                                                                            string partitionId,
                                                                            EventPosition eventPosition,
                                                                            EventHubConsumerOptions consumerOptions,
                                                                            TimeSpan timeout,
                                                                            CancellationToken cancellationToken)
        {
            Argument.AssertNotNullOrEmpty(consumerGroup, nameof(consumerGroup));
            Argument.AssertNotNullOrEmpty(partitionId, nameof(partitionId));
            Argument.AssertNotNull(eventPosition, nameof(eventPosition));
            Argument.AssertNotNull(consumerOptions, nameof(consumerOptions));

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var stopWatch        = Stopwatch.StartNew();
            var consumerEndpoint = new Uri(ServiceEndpoint, string.Format(ConsumerPathSuffixMask, consumerGroup, partitionId));

            var connection = await ActiveConnection.GetOrCreateAsync(timeout).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var link = await CreateReceivingLinkAsync(connection, consumerEndpoint, eventPosition, consumerOptions, timeout.CalculateRemaining(stopWatch.Elapsed), cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            await OpenAmqpObjectAsync(link, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            stopWatch.Stop();
            return(link);
        }
        public void CreateConsumerCreatesDefaultWhenNoOptionsArePassed()
        {
            var clientOptions = new EventHubClientOptions
            {
                Retry          = new ExponentialRetry(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), 5),
                DefaultTimeout = TimeSpan.FromHours(24)
            };

            var expectedOptions = new EventHubConsumerOptions
            {
                Retry = clientOptions.Retry,
                DefaultMaximumReceiveWaitTime = clientOptions.DefaultTimeout
            };

            var expectedConsumerGroup = EventHubConsumer.DefaultConsumerGroup;
            var expectedPartition     = "56767";
            var expectedPosition      = EventPosition.FromEnqueuedTime(DateTime.Parse("2015-10-27T12:00:00Z"));
            var connectionString      = "Endpoint=value.com;SharedAccessKeyName=[value];SharedAccessKey=[value];EntityPath=[value]";
            var mockClient            = new ReadableOptionsMock(connectionString, clientOptions);

            mockClient.CreateConsumer(expectedConsumerGroup, expectedPartition, expectedPosition);
            var actualOptions = mockClient.ConsumerOptions;

            Assert.That(actualOptions, Is.Not.Null, "The consumer options should have been set.");
            Assert.That(actualOptions.OwnerLevel, Is.EqualTo(expectedOptions.OwnerLevel), "The owner levels should match.");
            Assert.That(actualOptions.Identifier, Is.EqualTo(expectedOptions.Identifier), "The identifiers should match.");
            Assert.That(actualOptions.PrefetchCount, Is.EqualTo(expectedOptions.PrefetchCount), "The prefetch counts should match.");
            Assert.That(ExponentialRetry.HaveSameConfiguration((ExponentialRetry)actualOptions.Retry, (ExponentialRetry)expectedOptions.Retry), "The retries should match.");
            Assert.That(actualOptions.MaximumReceiveWaitTimeOrDefault, Is.EqualTo(expectedOptions.MaximumReceiveWaitTimeOrDefault), "The wait times should match.");
        }
        public void CreateConsumerCreatesDefaultWhenOptionsAreNotSet()
        {
            var clientOptions = new EventHubClientOptions
            {
                Retry          = new ExponentialRetry(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3), 5),
                DefaultTimeout = TimeSpan.FromHours(24)
            };

            var expectedOptions = new EventHubConsumerOptions
            {
                OwnerLevel    = 251,
                Identifier    = "Bob",
                PrefetchCount = 600,
                Retry         = clientOptions.Retry,
                DefaultMaximumReceiveWaitTime = clientOptions.DefaultTimeout
            };

            var expectedConsumerGroup = "SomeGroup";
            var expectedPartition     = "56767";
            var expectedPosition      = EventPosition.FromSequenceNumber(123);
            var connectionString      = "Endpoint=value.com;SharedAccessKeyName=[value];SharedAccessKey=[value];EntityPath=[value]";
            var mockClient            = new ReadableOptionsMock(connectionString, clientOptions);

            mockClient.CreateConsumer(expectedConsumerGroup, expectedPartition, expectedPosition, expectedOptions);
            var actualOptions = mockClient.ConsumerOptions;

            Assert.That(actualOptions, Is.Not.Null, "The consumer options should have been set.");
            Assert.That(actualOptions, Is.Not.SameAs(expectedOptions), "A clone of the options should have been made.");
            Assert.That(actualOptions.OwnerLevel, Is.EqualTo(expectedOptions.OwnerLevel), "The owner levels should match.");
            Assert.That(actualOptions.Identifier, Is.EqualTo(expectedOptions.Identifier), "The identifiers should match.");
            Assert.That(actualOptions.PrefetchCount, Is.EqualTo(expectedOptions.PrefetchCount), "The prefetch counts should match.");
            Assert.That(ExponentialRetry.HaveSameConfiguration((ExponentialRetry)actualOptions.Retry, (ExponentialRetry)expectedOptions.Retry), "The retries should match.");
            Assert.That(actualOptions.MaximumReceiveWaitTimeOrDefault, Is.EqualTo(expectedOptions.MaximumReceiveWaitTimeOrDefault), "The wait times should match.");
        }
        public void CreateConsumerInvokesTheTransportClient()
        {
            var transportClient = new ObservableTransportClientMock();
            var client          = new InjectableTransportClientMock(transportClient, "Endpoint=sb://not-real.servicebus.windows.net/;SharedAccessKeyName=DummyKey;SharedAccessKey=[not_real];EntityPath=fake");
            var expectedOptions = new EventHubConsumerOptions {
                Retry = Retry.Default
            };
            var expectedPosition      = EventPosition.FromOffset(65);
            var expectedPartition     = "2123";
            var expectedConsumerGroup = EventHubConsumer.DefaultConsumerGroup;

            client.CreateConsumer(expectedConsumerGroup, expectedPartition, expectedPosition, expectedOptions);
            (var actualConsumerGroup, var actualPartition, var actualPosition, var actualOptions) = transportClient.CreateConsumerCalledWith;

            Assert.That(actualPartition, Is.EqualTo(expectedPartition), "The partition should have been passed.");
            Assert.That(actualConsumerGroup, Is.EqualTo(expectedConsumerGroup), "The consumer groups should match.");
            Assert.That(actualPosition.Offset, Is.EqualTo(expectedPosition.Offset), "The event position to receive should match.");
            Assert.That(actualOptions, Is.Not.Null, "The consumer options should have been set.");
            Assert.That(actualPosition.Offset, Is.EqualTo(expectedPosition.Offset), "The event position to receive should match.");
            Assert.That(actualOptions.OwnerLevel, Is.EqualTo(expectedOptions.OwnerLevel), "The owner levels should match.");
            Assert.That(actualOptions.Identifier, Is.EqualTo(expectedOptions.Identifier), "The identifiers should match.");
            Assert.That(actualOptions.PrefetchCount, Is.EqualTo(expectedOptions.PrefetchCount), "The prefetch counts should match.");
            Assert.That(ExponentialRetry.HaveSameConfiguration((ExponentialRetry)actualOptions.Retry, (ExponentialRetry)expectedOptions.Retry), "The retries should match.");
            Assert.That(actualOptions.MaximumReceiveWaitTimeOrDefault, Is.EqualTo(expectedOptions.MaximumReceiveWaitTimeOrDefault), "The wait times should match.");
        }
示例#8
0
        /// <summary>
        ///   Creates a consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
        ///   and as a member of a specific consumer group.
        ///
        ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
        ///   group to ensure that only one consumer from that group is reading the from the partition.
        ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
        ///
        ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
        ///   group to be actively reading events from the partition.  These non-exclusive consumers are
        ///   sometimes referred to as "Non-epoch Consumers."
        ///
        ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
        ///   By default, consumers are created as non-exclusive.
        /// </summary>
        ///
        /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
        /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
        /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="consumerOptions" />.</param>
        ///
        /// <returns>An Event Hub consumer configured in the requested manner.</returns>
        ///
        public override EventHubConsumer CreateConsumer(string consumerGroup,
                                                        string partitionId,
                                                        EventPosition eventPosition,
                                                        EventHubConsumerOptions consumerOptions,
                                                        EventHubRetryPolicy defaultRetryPolicy)
        {
            TrackOne.PartitionReceiver CreateReceiverFactory(EventHubRetryPolicy activeRetryPolicy)
            {
                var position = new TrackOne.EventPosition
                {
                    IsInclusive     = eventPosition.IsInclusive,
                    Offset          = eventPosition.Offset,
                    SequenceNumber  = eventPosition.SequenceNumber,
                    EnqueuedTimeUtc = eventPosition.EnqueuedTime?.UtcDateTime
                };

                var trackOneOptions = new TrackOne.ReceiverOptions
                {
                    Identifier = consumerOptions.Identifier,
                    EnableReceiverRuntimeMetric = consumerOptions.TrackLastEnqueuedEventInformation
                };

                PartitionReceiver consumer;

                if (consumerOptions.OwnerLevel.HasValue)
                {
                    consumer = TrackOneClient.CreateEpochReceiver(consumerGroup, partitionId, position, consumerOptions.OwnerLevel.Value, trackOneOptions);
                }
                else
                {
                    consumer = TrackOneClient.CreateReceiver(consumerGroup, partitionId, position, trackOneOptions);
                }

                consumer.RetryPolicy = new TrackOneRetryPolicy(activeRetryPolicy);

                return(consumer);
            }

            EventHubRetryPolicy initialRetryPolicy = (consumerOptions.RetryOptions != null)
                ? new BasicRetryPolicy(consumerOptions.RetryOptions)
                : defaultRetryPolicy;

            LastEnqueuedEventProperties partitionMetrics = (consumerOptions.TrackLastEnqueuedEventInformation)
                ? new LastEnqueuedEventProperties(EventHubName, partitionId)
                : null;

            return(new EventHubConsumer
                   (
                       new TrackOneEventHubConsumer(CreateReceiverFactory, initialRetryPolicy, partitionMetrics),
                       TrackOneClient.EventHubName,
                       partitionId,
                       consumerGroup,
                       eventPosition,
                       consumerOptions,
                       initialRetryPolicy
                   ));
        }
示例#9
0
        public void DefaultMaximumReceiveWaitTimeUsesNormalizedValueIfNotSpecified(int?noTimeoutValue)
        {
            var options      = new EventHubConsumerOptions();
            var timeoutValue = (noTimeoutValue.HasValue) ? TimeSpan.Zero : (TimeSpan?)null;

            options.DefaultMaximumReceiveWaitTime = timeoutValue;
            Assert.That(options.DefaultMaximumReceiveWaitTime, Is.EqualTo(timeoutValue), "The value supplied by the caller should be preserved.");
            Assert.That(options.MaximumReceiveWaitTimeOrDefault, Is.Null, "The maximum wait value should be normalized to null internally.");
        }
        public void SettingTheRetryPolicyUpdatesTheTransportConsumer()
        {
            var customRetry       = Mock.Of <EventHubRetryPolicy>();
            var transportConsumer = new ObservableTransportConsumerMock();
            var consumerOptions   = new EventHubConsumerOptions();
            var consumer          = new EventHubConsumer(transportConsumer, "dummy", "consumerGroup", "0", EventPosition.Latest, consumerOptions, Mock.Of <EventHubRetryPolicy>());

            consumer.RetryPolicy = customRetry;
            Assert.That(transportConsumer.UpdateRetryPolicyCalledWith, Is.SameAs(customRetry), "The custom retry policy should have been set.");
        }
        public void ConstructorSetsThePriority(long?priority)
        {
            var options = new EventHubConsumerOptions
            {
                OwnerLevel = priority
            };

            var transportConsumer = new ObservableTransportConsumerMock();
            var consumer          = new EventHubConsumer(transportConsumer, "dummy", EventHubConsumer.DefaultConsumerGroupName, "0", EventPosition.FromOffset(65), options, Mock.Of <EventHubRetryPolicy>());

            Assert.That(consumer.OwnerLevel, Is.EqualTo(priority));
        }
        public void ConstructorSetsTheConsumerGroup()
        {
            var options = new EventHubConsumerOptions
            {
                ConsumerGroup = "SomeGroup"
            };

            var transportConsumer = new ObservableTransportConsumerMock();
            var consumer          = new EventHubConsumer(transportConsumer, "dummy", "0", EventPosition.Latest, options);

            Assert.That(consumer.ConsumerGroup, Is.EqualTo(options.ConsumerGroup));
        }
        /// <summary>
        ///   Creates a consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
        ///   and as a member of a specific consumer group.
        ///
        ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
        ///   group to ensure that only one consumer from that group is reading the from the partition.
        ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
        ///
        ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
        ///   group to be actively reading events from the partition.  These non-exclusive consumers are
        ///   sometimes referred to as "Non-epoch Consumers."
        ///
        ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
        ///   By default, consumers are created as non-exclusive.
        /// </summary>
        ///
        /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
        /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
        /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
        ///
        /// <returns>An Event Hub consumer configured in the requested manner.</returns>
        ///
        public override EventHubConsumer CreateConsumer(string consumerGroup,
                                                        string partitionId,
                                                        EventPosition eventPosition,
                                                        EventHubConsumerOptions consumerOptions)
        {
            TrackOne.PartitionReceiver CreateReceiverFactory()
            {
                var position = new TrackOne.EventPosition
                {
                    IsInclusive     = eventPosition.IsInclusive,
                    Offset          = eventPosition.Offset,
                    SequenceNumber  = eventPosition.SequenceNumber,
                    EnqueuedTimeUtc = eventPosition.EnqueuedTime?.UtcDateTime
                };

                var trackOneOptions = new TrackOne.ReceiverOptions {
                    Identifier = consumerOptions.Identifier
                };

                PartitionReceiver consumer;

                if (consumerOptions.OwnerLevel.HasValue)
                {
                    consumer = TrackOneClient.CreateEpochReceiver(consumerGroup, partitionId, position, consumerOptions.OwnerLevel.Value, trackOneOptions);
                }
                else
                {
                    consumer = TrackOneClient.CreateReceiver(consumerGroup, partitionId, position, trackOneOptions);
                }

                (TimeSpan minBackoff, TimeSpan maxBackoff, int maxRetries) = ((ExponentialRetry)consumerOptions.Retry).GetProperties();
                consumer.RetryPolicy = new RetryExponential(minBackoff, maxBackoff, maxRetries);

                return(consumer);
            }

            return(new EventHubConsumer
                   (
                       new TrackOneEventHubConsumer(CreateReceiverFactory),
                       TrackOneClient.EventHubName,
                       partitionId,
                       consumerGroup,
                       eventPosition,
                       consumerOptions
                   ));
        }
示例#14
0
        public void ReceiveAsyncRespectsTheRetryPolicy(RetryOptions retryOptions)
        {
            var eventHub      = "eventHubName";
            var consumerGroup = "$DEFAULT";
            var partition     = "3";
            var eventPosition = EventPosition.FromOffset(123);
            var options       = new EventHubConsumerOptions {
                Identifier = "OMG!"
            };
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = new EventHubsException(true, "Test");
            var mockConverter      = new Mock <AmqpMessageConverter>();
            var mockCredential     = new Mock <TokenCredential>();
            var mockScope          = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(Task.FromResult(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockScope
            .Setup(scope => scope.OpenConsumerLinkAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <EventPosition>(),
                       It.IsAny <EventHubConsumerOptions>(),
                       It.IsAny <TimeSpan>(),
                       It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var consumer = new AmqpEventHubConsumer(eventHub, consumerGroup, partition, eventPosition, options, mockScope.Object, Mock.Of <AmqpMessageConverter>(), retryPolicy, null);

            Assert.That(async() => await consumer.ReceiveAsync(100, null, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope
            .Verify(scope => scope.OpenConsumerLinkAsync(
                        It.Is <string>(value => value == consumerGroup),
                        It.Is <string>(value => value == partition),
                        It.Is <EventPosition>(value => value == eventPosition),
                        It.Is <EventHubConsumerOptions>(value => value == options),
                        It.IsAny <TimeSpan>(),
                        It.IsAny <CancellationToken>()),
                    Times.Exactly(1 + retryOptions.MaximumRetries));
        }
        public async Task ReceiveAsyncInvokesTheTransportConsumer()
        {
            var options = new EventHubConsumerOptions {
                DefaultMaximumReceiveWaitTime = TimeSpan.FromMilliseconds(8)
            };
            var transportConsumer    = new ObservableTransportConsumerMock();
            var consumer             = new EventHubConsumer(transportConsumer, "dummy", EventHubConsumer.DefaultConsumerGroupName, "0", EventPosition.Latest, options, Mock.Of <EventHubRetryPolicy>());
            var cancellation         = new CancellationTokenSource();
            var expectedMessageCount = 45;

            await consumer.ReceiveAsync(expectedMessageCount, null, cancellation.Token);

            (var actualMessageCount, var actualWaitTime) = transportConsumer.ReceiveCalledWith;

            Assert.That(actualMessageCount, Is.EqualTo(expectedMessageCount), "The message counts should match.");
            Assert.That(actualWaitTime, Is.EqualTo(options.DefaultMaximumReceiveWaitTime), "The wait time should match.");
        }
示例#16
0
        public async Task ConsumerWithOptionsCanReceive()
        {
            await using (var scope = await EventHubScope.CreateAsync(4))
            {
                var connectionString = TestEnvironment.BuildConnectionStringForEventHub(scope.EventHubName);
                var options          = new EventHubConsumerOptions {
                    Identifier = "FakeIdentifier"
                };

                await using (var client = new EventHubClient(connectionString))
                {
                    var partition = (await client.GetPartitionIdsAsync()).First();

                    await using (var consumer = client.CreateConsumer(EventHubConsumer.DefaultConsumerGroup, partition, EventPosition.Latest, options))
                    {
                        Assert.That(async() => await consumer.ReceiveAsync(1, TimeSpan.Zero), Throws.Nothing);
                    }
                }
            }
        }
        public void CloneProducesACopy()
        {
            var options = new EventHubConsumerOptions
            {
                OwnerLevel = 99,
                Retry      = new ExponentialRetry(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(5), 6),
                DefaultMaximumReceiveWaitTime = TimeSpan.FromMinutes(65),
                Identifier = "an_event_consumer"
            };

            var clone = options.Clone();

            Assert.That(clone, Is.Not.Null, "The clone should not be null.");

            Assert.That(clone.OwnerLevel, Is.EqualTo(options.OwnerLevel), "The ownerlevel of the clone should match.");
            Assert.That(clone.DefaultMaximumReceiveWaitTime, Is.EqualTo(options.DefaultMaximumReceiveWaitTime), "The default maximum wait time of the clone should match.");
            Assert.That(clone.Identifier, Is.EqualTo(options.Identifier), "The identifier of the clone should match.");

            Assert.That(ExponentialRetry.HaveSameConfiguration((ExponentialRetry)clone.Retry, (ExponentialRetry)options.Retry), "The retry of the clone should be considered equal.");
            Assert.That(clone.Retry, Is.Not.SameAs(options.Retry), "The retry of the clone should be a copy, not the same instance.");
        }
示例#18
0
        public void ReceiveAsyncValidatesTheMaximumMessageCount(int count)
        {
            var eventHub      = "eventHubName";
            var consumerGroup = "$DEFAULT";
            var partition     = "3";
            var eventPosition = EventPosition.FromOffset(123);
            var options       = new EventHubConsumerOptions {
                Identifier = "OMG!"
            };
            var retryPolicy        = new BasicRetryPolicy(new RetryOptions());
            var retriableException = new EventHubsException(true, "Test");
            var mockConverter      = new Mock <AmqpMessageConverter>();
            var mockCredential     = new Mock <TokenCredential>();
            var mockScope          = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            var consumer = new AmqpEventHubConsumer(eventHub, consumerGroup, partition, eventPosition, options, mockScope.Object, Mock.Of <AmqpMessageConverter>(), retryPolicy, null);

            Assert.That(async() => await consumer.ReceiveAsync(count, null, cancellationSource.Token), Throws.InstanceOf <ArgumentException>());
        }
示例#19
0
        public void CloneProducesACopy()
        {
            var options = new EventHubConsumerOptions
            {
                OwnerLevel   = 99,
                RetryOptions = new RetryOptions {
                    Mode = RetryMode.Fixed
                },
                DefaultMaximumReceiveWaitTime = TimeSpan.FromMinutes(65),
                Identifier = "an_event_consumer"
            };

            var clone = options.Clone();

            Assert.That(clone, Is.Not.Null, "The clone should not be null.");

            Assert.That(clone.OwnerLevel, Is.EqualTo(options.OwnerLevel), "The owner level of the clone should match.");
            Assert.That(clone.DefaultMaximumReceiveWaitTime, Is.EqualTo(options.DefaultMaximumReceiveWaitTime), "The default maximum wait time of the clone should match.");
            Assert.That(clone.Identifier, Is.EqualTo(options.Identifier), "The identifier of the clone should match.");

            Assert.That(clone.RetryOptions.IsEquivalentTo(options.RetryOptions), Is.True, "The retry options of the clone should be considered equal.");
            Assert.That(clone.RetryOptions, Is.Not.SameAs(options.RetryOptions), "The retry options of the clone should be a copy, not the same instance.");
        }
        /// <summary>
        ///   Creates an Event Hub consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
        ///   and as a member of a specific consumer group.
        ///
        ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
        ///   group to ensure that only one consumer from that group is reading the from the partition.
        ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
        ///
        ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
        ///   group to be actively reading events from the partition.  These non-exclusive consumers are
        ///   sometimes referred to as "Non-epoch Consumers."
        ///
        ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
        ///   By default, consumers are created as non-exclusive.
        /// </summary>
        ///
        /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
        /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
        /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="consumerOptions" />.</param>
        ///
        /// <returns>An Event Hub consumer configured in the requested manner.</returns>
        ///
        public override EventHubConsumer CreateConsumer(string consumerGroup,
                                                        string partitionId,
                                                        EventPosition eventPosition,
                                                        EventHubConsumerOptions consumerOptions,
                                                        EventHubRetryPolicy defaultRetryPolicy)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpEventHubClient));

            EventHubRetryPolicy retryPolicy = defaultRetryPolicy ?? _retryPolicy;

            var transportConsumer = new AmqpEventHubConsumer
                                    (
                EventHubName,
                consumerGroup,
                partitionId,
                eventPosition,
                consumerOptions,
                ConnectionScope,
                MessageConverter,
                retryPolicy
                                    );

            return(new EventHubConsumer(transportConsumer, EventHubName, consumerGroup, partitionId, eventPosition, consumerOptions, retryPolicy));
        }
 public override EventHubConsumer CreateConsumer(string consumerGroup, string partitionId, EventPosition eventPosition, EventHubConsumerOptions consumerOptions)
 {
     CreateConsumerCalledWith = (consumerGroup, partitionId, eventPosition, consumerOptions);
     return(default(EventHubConsumer));
 }
示例#22
0
        /// <summary>
        ///   Creates an AMQP link for use with receiving operations.
        /// </summary>
        ///
        /// <param name="connection">The active and opened AMQP connection to use for this link.</param>
        /// <param name="endpoint">The fully qualified endpoint to open the link for.</param>
        /// <param name="eventPosition">The position of the event in the partition where the link should be filtered to.</param>
        /// <param name="consumerOptions">The set of active options for the consumer that will make use of the link.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>A link for use for operations related to receiving events.</returns>
        ///
        protected virtual async Task <ReceivingAmqpLink> CreateReceivingLinkAsync(AmqpConnection connection,
                                                                                  Uri endpoint,
                                                                                  EventPosition eventPosition,
                                                                                  EventHubConsumerOptions consumerOptions,
                                                                                  TimeSpan timeout,
                                                                                  CancellationToken cancellationToken)
        {
            Argument.AssertNotDisposed(IsDisposed, nameof(AmqpConnectionScope));
            cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

            var session   = default(AmqpSession);
            var stopWatch = Stopwatch.StartNew();

            try
            {
                // Perform the initial authorization for the link.

                var authClaims        = new[] { EventHubsClaim.Listen };
                var authExpirationUtc = await RequestAuthorizationUsingCbsAsync(connection, TokenProvider, endpoint, endpoint.AbsoluteUri, endpoint.AbsoluteUri, authClaims, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the AMQP session associated with the link.

                var sessionSettings = new AmqpSessionSettings {
                    Properties = new Fields()
                };
                session = connection.CreateSession(sessionSettings);

                await OpenAmqpObjectAsync(session, timeout).ConfigureAwait(false);

                cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                // Create and open the link.

                var filters = new FilterSet();
                filters.Add(AmqpFilter.ConsumerFilterName, AmqpFilter.CreateConsumerFilter(AmqpFilter.BuildFilterExpression(eventPosition)));

                var linkSettings = new AmqpLinkSettings
                {
                    Role            = true,
                    TotalLinkCredit = (uint)consumerOptions.PrefetchCount,
                    AutoSendFlow    = consumerOptions.PrefetchCount > 0,
                    SettleType      = SettleMode.SettleOnSend,
                    Source          = new Source {
                        Address = endpoint.AbsolutePath, FilterSet = filters
                    },
                    Target = new Target {
                        Address = Guid.NewGuid().ToString()
                    }
                };

                linkSettings.AddProperty(AmqpProperty.EntityType, (int)AmqpProperty.Entity.ConsumerGroup);

                if (!string.IsNullOrEmpty(consumerOptions.Identifier))
                {
                    linkSettings.AddProperty(AmqpProperty.ConsumerIdentifier, consumerOptions.Identifier);
                }

                if (consumerOptions.OwnerLevel.HasValue)
                {
                    linkSettings.AddProperty(AmqpProperty.OwnerLevel, consumerOptions.OwnerLevel.Value);
                }

                if (consumerOptions.TrackLastEnqueuedEventInformation)
                {
                    linkSettings.DesiredCapabilities = new Multiple <AmqpSymbol>(new List <AmqpSymbol>
                    {
                        AmqpProperty.TrackLastEnqueuedEventInformation
                    });
                }

                var link = new ReceivingAmqpLink(linkSettings);
                linkSettings.LinkName = $"{ Id };{ connection.Identifier };{ session.Identifier };{ link.Identifier }";
                link.AttachTo(session);

                stopWatch.Stop();

                // Configure refresh for authorization of the link.

                var refreshTimer = default(Timer);

                var refreshHandler = CreateAuthorizationRefreshHandler
                                     (
                    connection,
                    link,
                    TokenProvider,
                    endpoint,
                    endpoint.AbsoluteUri,
                    endpoint.AbsoluteUri,
                    authClaims,
                    AuthorizationRefreshTimeout,
                    () => refreshTimer
                                     );

                refreshTimer = new Timer(refreshHandler, null, CalculateLinkAuthorizationRefreshInterval(authExpirationUtc), Timeout.InfiniteTimeSpan);

                // Track the link before returning it, so that it can be managed with the scope.

                BeginTrackingLinkAsActive(link, refreshTimer);
                return(link);
            }
            catch
            {
                // Aborting the session will perform any necessary cleanup of
                // the associated link as well.

                session?.Abort();
                throw;
            }
        }
 /// <summary>
 ///   Creates an Event Hub consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
 ///   and as a member of a specific consumer group.
 ///
 ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
 ///   group to ensure that only one consumer from that group is reading the from the partition.
 ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
 ///
 ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
 ///   group to be actively reading events from the partition.  These non-exclusive consumers are
 ///   sometimes referred to as "Non-epoch Consumers."
 ///
 ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
 ///   By default, consumers are created as non-exclusive.
 /// </summary>
 ///
 /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
 /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
 /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
 ///
 /// <returns>An Event Hub consumer configured in the requested manner.</returns>
 ///
 public abstract EventHubConsumer CreateConsumer(string partitionId,
                                                 EventPosition eventPosition,
                                                 EventHubConsumerOptions consumerOptions);
示例#24
0
 /// <summary>
 ///   Creates an Event Hub consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
 ///   and as a member of a specific consumer group.
 ///
 ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
 ///   group to ensure that only one consumer from that group is reading the from the partition.
 ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
 ///
 ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
 ///   group to be actively reading events from the partition.  These non-exclusive consumers are
 ///   sometimes referred to as "Non-epoch Consumers."
 ///
 ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
 ///   By default, consumers are created as non-exclusive.
 /// </summary>
 ///
 /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
 /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
 /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
 /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
 /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="consumerOptions" />.</param>
 ///
 /// <returns>An Event Hub consumer configured in the requested manner.</returns>
 ///
 public abstract EventHubConsumer CreateConsumer(string consumerGroup,
                                                 string partitionId,
                                                 EventPosition eventPosition,
                                                 EventHubConsumerOptions consumerOptions,
                                                 EventHubRetryPolicy defaultRetryPolicy);
示例#25
0
 /// <summary>
 ///   Creates an Event Hub consumer responsible for reading <see cref="EventData" /> from a specific Event Hub partition,
 ///   and as a member of a specific consumer group.
 ///
 ///   A consumer may be exclusive, which asserts ownership over the partition for the consumer
 ///   group to ensure that only one consumer from that group is reading the from the partition.
 ///   These exclusive consumers are sometimes referred to as "Epoch Consumers."
 ///
 ///   A consumer may also be non-exclusive, allowing multiple consumers from the same consumer
 ///   group to be actively reading events from the partition.  These non-exclusive consumers are
 ///   sometimes referred to as "Non-epoch Consumers."
 ///
 ///   Designating a consumer as exclusive may be specified in the <paramref name="consumerOptions" />.
 ///   By default, consumers are created as non-exclusive.
 /// </summary>
 ///
 /// <param name="consumerGroup">The name of the consumer group this consumer is associated with.  Events are read in the context of this group.</param>
 /// <param name="partitionId">The identifier of the Event Hub partition from which events will be received.</param>
 /// <param name="eventPosition">The position within the partition where the consumer should begin reading events.</param>
 /// <param name="consumerOptions">The set of options to apply when creating the consumer.</param>
 /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="consumerOptions" />.</param>
 ///
 /// <returns>An Event Hub consumer configured in the requested manner.</returns>
 ///
 public override EventHubConsumer CreateConsumer(string consumerGroup,
                                                 string partitionId,
                                                 EventPosition eventPosition,
                                                 EventHubConsumerOptions consumerOptions,
                                                 EventHubRetryPolicy defaultRetryPolicy) => throw new NotImplementedException();