/// <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."); }
/// <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)); }
/// <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."); }
/// <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 )); }
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 )); }
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."); }
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."); }
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>()); }
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)); }
/// <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);
/// <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);
/// <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();