public void ConstructorSetsTheRetryPolicy()
        {
            EventHubRetryPolicy retryPolicy = Mock.Of <EventHubRetryPolicy>();
            var producer = new EventHubProducer(new ObservableTransportProducerMock(), new Uri("amqp://some.endpoint.com/path"), "path", new EventHubProducerOptions(), retryPolicy);

            Assert.That(producer.RetryPolicy, Is.SameAs(retryPolicy));
        }
        public void SettingTheRetryUpdatesState()
        {
            var retryOptions = new RetryOptions
            {
                Delay          = TimeSpan.FromSeconds(1),
                MaximumDelay   = TimeSpan.FromSeconds(2),
                TryTimeout     = TimeSpan.FromSeconds(3),
                MaximumRetries = 4,
                Mode           = RetryMode.Fixed
            };

            EventHubRetryPolicy customRetry = Mock.Of <EventHubRetryPolicy>();
            var producerOptions             = new EventHubProducerOptions {
                RetryOptions = retryOptions
            };
            var producer = new EventHubProducer(new ObservableTransportProducerMock(), new Uri("amqp://some.endpoint.com/path"), "dummy", producerOptions, new BasicRetryPolicy(retryOptions));

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

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

            var activeOptions = (EventHubProducerOptions)
                                typeof(EventHubProducer)
                                .GetProperty("Options", BindingFlags.Instance | BindingFlags.NonPublic)
                                .GetValue(producer);

            Assert.That(activeOptions.RetryOptions, Is.Null, "Setting a custom policy should clear the retry options.");
        }
Example #3
0
        /// <summary>
        ///   Updates the active retry policy for the client.
        /// </summary>
        ///
        /// <param name="newRetryPolicy">The retry policy to set as active.</param>
        ///
        public override void UpdateRetryPolicy(EventHubRetryPolicy newRetryPolicy)
        {
            Argument.AssertNotNull(newRetryPolicy, nameof(newRetryPolicy));

            _retryPolicy = newRetryPolicy;
            _tryTimeout  = _retryPolicy.CalculateTryTimeout(0);
        }
 internal override Task <PartitionProperties> GetPartitionPropertiesAsync(string partitionId,
                                                                          EventHubRetryPolicy retryPolicy,
                                                                          CancellationToken cancellationToken = default)
 {
     GetPartitionPropertiesInvokedWith = retryPolicy;
     return(Task.FromResult(default(PartitionProperties)));
 }
Example #5
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);
        }
Example #6
0
        /// <summary>
        ///   Updates the active retry policy for the client.
        /// </summary>
        ///
        /// <param name="newRetryPolicy">The retry policy to set as active.</param>
        ///
        public override void UpdateRetryPolicy(EventHubRetryPolicy newRetryPolicy)
        {
            Guard.ArgumentNotNull(nameof(newRetryPolicy), newRetryPolicy);

            _retryPolicy = newRetryPolicy;
            _tryTimeout  = _retryPolicy.CalculateTryTimeout(0);
        }
 public override Task <PartitionProperties> GetPartitionPropertiesAsync(string partitionId,
                                                                        EventHubRetryPolicy retryPolicy,
                                                                        CancellationToken cancellationToken = default)
 {
     GetPartitionPropertiesCalledForId = partitionId;
     return(Task.FromResult(default(PartitionProperties)));
 }
Example #8
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));
        }
        /// <summary>
        ///   Creates an Event Hub producer responsible for transmitting <see cref="EventData" /> to the
        ///   Event Hub, grouped together in batches.  Depending on the <paramref name="producerOptions"/>
        ///   specified, the producer may be created to allow event data to be automatically routed to an available
        ///   partition or specific to a partition.
        /// </summary>
        ///
        /// <param name="producerOptions">The set of options to apply when creating the producer.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="producerOptions" />.</param>
        ///
        /// <returns>An Event Hub producer configured in the requested manner.</returns>
        ///
        public override EventHubProducer CreateProducer(EventHubProducerOptions producerOptions,
                                                        EventHubRetryPolicy defaultRetryPolicy)
        {
            TrackOne.EventDataSender CreateSenderFactory(EventHubRetryPolicy activeRetryPolicy)
            {
                var producer = TrackOneClient.CreateEventSender(producerOptions.PartitionId);

                producer.RetryPolicy = new TrackOneRetryPolicy(activeRetryPolicy);

                return(producer);
            }

            var initialRetryPolicy = (producerOptions.RetryOptions != null)
                ? new BasicRetryPolicy(producerOptions.RetryOptions)
                : defaultRetryPolicy;

            return(new EventHubProducer
                   (
                       new TrackOneEventHubProducer(CreateSenderFactory, initialRetryPolicy),
                       TrackOneClient.ConnectionStringBuilder.Endpoint,
                       TrackOneClient.EventHubName,
                       producerOptions,
                       initialRetryPolicy
                   ));
        }
 /// <summary>
 ///   Initializes a new instance of the <see cref="TrackOneEventHubClient"/> class.
 /// </summary>
 ///
 /// <param name="host">The fully qualified host name for the Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
 /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
 /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls may be specified by the Event Hubs namespace or the requested Event Hub, depending on Azure configuration.</param>
 /// <param name="clientOptions">A set of options to apply when configuring the client.</param>
 /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="clientOptions" />.</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 TrackOneEventHubClient(string host,
                               string eventHubName,
                               TokenCredential credential,
                               EventHubClientOptions clientOptions,
                               EventHubRetryPolicy defaultRetryPolicy) : this(host, eventHubName, credential, clientOptions, defaultRetryPolicy, CreateClient)
 {
 }
Example #11
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="AmqpEventHubClient"/> class.
 /// </summary>
 ///
 /// <param name="host">The fully qualified host name for the Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
 /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
 /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls may be specified by the Event Hubs namespace or the requested Event Hub, depending on Azure configuration.</param>
 /// <param name="clientOptions">A set of options to apply when configuring the client.</param>
 /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="clientOptions" />.</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 AmqpEventHubClient(string host,
                           string eventHubName,
                           TokenCredential credential,
                           EventHubClientOptions clientOptions,
                           EventHubRetryPolicy defaultRetryPolicy) : this(host, eventHubName, credential, clientOptions, defaultRetryPolicy, null, null)
 {
 }
        public async Task ReceiverIsConstructedCorrectly()
        {
            var eventHub                    = "eventHub";
            var consumerGroup               = "$TestThing";
            var partition                   = "123";
            var position                    = EventPosition.FromEnqueuedTime(DateTimeOffset.Parse("2015-10-25T12:00:00Z"));
            var priority                    = 8765;
            var identifier                  = "ThisIsAnAwesomeConsumer!";
            var partitionMetrics            = new LastEnqueuedEventProperties(eventHub, partition);
            EventHubRetryPolicy retryPolicy = Mock.Of <EventHubRetryPolicy>();
            var mock = new ObservableReceiverMock(new ClientMock(), consumerGroup, partition, TrackOne.EventPosition.FromEnqueuedTime(position.EnqueuedTime.Value.UtcDateTime), priority, new ReceiverOptions {
                Identifier = identifier, EnableReceiverRuntimeMetric = true
            });
            var consumer = new TrackOneEventHubConsumer(_ => mock, retryPolicy, partitionMetrics);

            // Invoke an operation to force the consumer to be lazily instantiated.  Otherwise,
            // construction does not happen.

            await consumer.ReceiveAsync(0, TimeSpan.Zero, default);

            Assert.That(mock.ConstructedWith.ConsumerGroup, Is.EqualTo(consumerGroup), "The consumer group should match.");
            Assert.That(mock.ConstructedWith.Partition, Is.EqualTo(partition), "The partition should match.");
            Assert.That(TrackOneComparer.IsEventPositionEquivalent(mock.ConstructedWith.Position, position), Is.True, "The starting event position should match.");
            Assert.That(mock.ConstructedWith.Priority, Is.EqualTo(priority), "The owner level should match.");
            Assert.That(mock.ConstructedWith.Options.Identifier, Is.EqualTo(identifier), "The consumer identifier should match.");
            Assert.That(mock.ConstructedWith.Options.EnableReceiverRuntimeMetric, Is.True, "The receiver metrics should be enabled when set in the options.");

            EventHubRetryPolicy consumerRetry = GetRetryPolicy(consumer);

            Assert.That(consumerRetry, Is.SameAs(retryPolicy), "The consumer retry instance should match.");
        }
Example #13
0
 public InjectableMockClient(string host,
                             string eventHubName,
                             TokenCredential credential,
                             EventHubClientOptions clientOptions,
                             EventHubRetryPolicy defaultRetryPolicy,
                             AmqpConnectionScope connectionScope,
                             AmqpMessageConverter messageConverter) : base(host, eventHubName, credential, clientOptions, defaultRetryPolicy, connectionScope, messageConverter)
 {
 }
        /// <summary>
        ///     Initializes a new instance of the <see cref="TrackOneEventHubProducer"/> class.
        /// </summary>
        ///
        /// <param name="trackOneSenderFactory">A delegate that can be used for creation of the <see cref="TrackOne.EventDataSender" /> to which operations are delegated to.</param>
        /// <param name="retryPolicy">The retry policy to use when creating the <see cref="TrackOne.EventDataSender" />.</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 TrackOneEventHubProducer(Func <EventHubRetryPolicy, TrackOne.EventDataSender> trackOneSenderFactory,
                                        EventHubRetryPolicy retryPolicy)
        {
            Argument.AssertNotNull(trackOneSenderFactory, nameof(trackOneSenderFactory));
            Argument.AssertNotNull(retryPolicy, nameof(retryPolicy));

            _retryPolicy    = retryPolicy;
            _trackOneSender = new Lazy <TrackOne.EventDataSender>(() => trackOneSenderFactory(_retryPolicy), LazyThreadSafetyMode.ExecutionAndPublication);
        }
Example #15
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
                   ));
        }
        /// <summary>
        ///   Updates the active retry policy for the client.
        /// </summary>
        ///
        /// <param name="newRetryPolicy">The retry policy to set as active.</param>
        ///
        public override void UpdateRetryPolicy(EventHubRetryPolicy newRetryPolicy)
        {
            _retryPolicy = newRetryPolicy;

            if (_trackOneSender.IsValueCreated)
            {
                TrackOneSender.RetryPolicy = new TrackOneRetryPolicy(newRetryPolicy);
            }
        }
Example #17
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="TrackOneEventHubConsumer"/> class.
        /// </summary>
        ///
        /// <param name="trackOneReceiverFactory">A delegate that can be used for creation of the <see cref="TrackOne.PartitionReceiver" /> to which operations are delegated to.</param>
        /// <param name="retryPolicy">The retry policy to use when creating the <see cref="TrackOne.PartitionReceiver" />.</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 TrackOneEventHubConsumer(Func <EventHubRetryPolicy, TrackOne.PartitionReceiver> trackOneReceiverFactory,
                                        EventHubRetryPolicy retryPolicy)
        {
            Guard.ArgumentNotNull(nameof(trackOneReceiverFactory), trackOneReceiverFactory);
            Guard.ArgumentNotNull(nameof(retryPolicy), retryPolicy);

            _retryPolicy      = retryPolicy;
            _trackOneReceiver = new Lazy <TrackOne.PartitionReceiver>(() => trackOneReceiverFactory(_retryPolicy), LazyThreadSafetyMode.PublicationOnly);
        }
        /// <summary>
        ///     Initializes a new instance of the <see cref="TrackOneEventHubProducer"/> class.
        /// </summary>
        ///
        /// <param name="trackOneSenderFactory">A delegate that can be used for creation of the <see cref="TrackOne.EventDataSender" /> to which operations are delegated to.</param>
        /// <param name="retryPolicy">The retry policy to use when creating the <see cref="TrackOne.EventDataSender" />.</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 TrackOneEventHubProducer(Func <EventHubRetryPolicy, TrackOne.EventDataSender> trackOneSenderFactory,
                                        EventHubRetryPolicy retryPolicy)
        {
            Guard.ArgumentNotNull(nameof(trackOneSenderFactory), trackOneSenderFactory);
            Guard.ArgumentNotNull(nameof(retryPolicy), retryPolicy);

            _retryPolicy    = retryPolicy;
            _trackOneSender = new Lazy <TrackOne.EventDataSender>(() => trackOneSenderFactory(_retryPolicy), LazyThreadSafetyMode.PublicationOnly);
        }
        /// <summary>
        ///   Updates the active retry policy for the client.
        /// </summary>
        ///
        /// <param name="newRetryPolicy">The retry policy to set as active.</param>
        ///
        public override void UpdateRetryPolicy(EventHubRetryPolicy newRetryPolicy)
        {
            _retryPolicy = newRetryPolicy;

            if (_trackOneClient.IsValueCreated)
            {
                TrackOneClient.RetryPolicy = new TrackOneRetryPolicy(newRetryPolicy);
                TrackOneClient.ConnectionStringBuilder.OperationTimeout = newRetryPolicy.CalculateTryTimeout(0);
            }
        }
Example #20
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="TrackOneEventHubConsumer"/> class.
        /// </summary>
        ///
        /// <param name="trackOneReceiverFactory">A delegate that can be used for creation of the <see cref="TrackOne.PartitionReceiver" /> to which operations are delegated to.</param>
        /// <param name="retryPolicy">The retry policy to use when creating the <see cref="TrackOne.PartitionReceiver" />.</param>
        /// <param name="lastEnqueuedEventProperties">The set of properties for the last event enqueued in a partition; if not requested in the consumer options, it is expected that this is <c>null</c>.</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 TrackOneEventHubConsumer(Func <EventHubRetryPolicy, TrackOne.PartitionReceiver> trackOneReceiverFactory,
                                        EventHubRetryPolicy retryPolicy,
                                        LastEnqueuedEventProperties lastEnqueuedEventProperties) : base(lastEnqueuedEventProperties)
        {
            Guard.ArgumentNotNull(nameof(trackOneReceiverFactory), trackOneReceiverFactory);
            Guard.ArgumentNotNull(nameof(retryPolicy), retryPolicy);

            _retryPolicy      = retryPolicy;
            _trackOneReceiver = new Lazy <TrackOne.PartitionReceiver>(() => trackOneReceiverFactory(_retryPolicy), LazyThreadSafetyMode.ExecutionAndPublication);
        }
        public void SettingTheRetryUpdatesTheTransportProducer()
        {
            EventHubRetryPolicy customRetry = Mock.Of <EventHubRetryPolicy>();
            var transportProducer           = new ObservableTransportProducerMock();
            var producerOptions             = new EventHubProducerOptions();
            var producer = new EventHubProducer(transportProducer, new Uri("amqp://some.endpoint.com/path"), "dummy", producerOptions, Mock.Of <EventHubRetryPolicy>())
            {
                RetryPolicy = customRetry
            };

            Assert.That(transportProducer.UpdateRetryPolicyCalledWith, Is.SameAs(customRetry), "The custom retry policy should have been passed to the transport producer.");
        }
        /// <summary>
        ///   Initializes a new instance of the <see cref="TrackOneEventHubClient"/> class.
        /// </summary>
        ///
        /// <param name="host">The fully qualified host name for the Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
        /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls may be specified by the Event Hubs namespace or the requested Event Hub, depending on Azure configuration.</param>
        /// <param name="clientOptions">A set of options to apply when configuring the client.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="clientOptions" />.</param>
        /// <param name="eventHubClientFactory">A delegate that can be used for creation of the <see cref="TrackOne.EventHubClient" /> to which operations are delegated to.</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 TrackOneEventHubClient(string host,
                                      string eventHubName,
                                      TokenCredential credential,
                                      EventHubClientOptions clientOptions,
                                      EventHubRetryPolicy defaultRetryPolicy,
                                      Func <string, string, TokenCredential, EventHubClientOptions, Func <EventHubRetryPolicy>, TrackOne.EventHubClient> eventHubClientFactory)
        {
            Guard.ArgumentNotNullOrEmpty(nameof(host), host);
            Guard.ArgumentNotNullOrEmpty(nameof(eventHubName), eventHubName);
            Guard.ArgumentNotNull(nameof(credential), credential);
            Guard.ArgumentNotNull(nameof(clientOptions), clientOptions);
            Guard.ArgumentNotNull(nameof(defaultRetryPolicy), defaultRetryPolicy);

            _retryPolicy    = defaultRetryPolicy;
            _trackOneClient = new Lazy <TrackOne.EventHubClient>(() => eventHubClientFactory(host, eventHubName, credential, clientOptions, () => _retryPolicy), LazyThreadSafetyMode.ExecutionAndPublication);
        }
Example #23
0
        /// <summary>
        ///   Creates an Event Hub producer responsible for transmitting <see cref="EventData" /> to the
        ///   Event Hub, grouped together in batches.  Depending on the <paramref name="producerOptions"/>
        ///   specified, the producer may be created to allow event data to be automatically routed to an available
        ///   partition or specific to a partition.
        /// </summary>
        ///
        /// <param name="producerOptions">The set of options to apply when creating the producer.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="producerOptions" />.</param>
        ///
        /// <returns>An Event Hub producer configured in the requested manner.</returns>
        ///
        public override EventHubProducer CreateProducer(EventHubProducerOptions producerOptions,
                                                        EventHubRetryPolicy defaultRetryPolicy)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpEventHubClient));

            EventHubRetryPolicy retryPolicy = defaultRetryPolicy ?? _retryPolicy;

            var transportProducer = new AmqpEventHubProducer
                                    (
                EventHubName,
                producerOptions.PartitionId,
                ConnectionScope,
                MessageConverter,
                retryPolicy
                                    );

            return(new EventHubProducer(transportProducer, ServiceEndpoint, EventHubName, producerOptions, retryPolicy));
        }
Example #24
0
        public async Task ProducerIsConstructedCorrectly()
        {
            var partition = "123";
            EventHubRetryPolicy retryPolicy = Mock.Of <EventHubRetryPolicy>();
            var mock     = new ObservableSenderMock(new ClientMock(), partition);
            var producer = new TrackOneEventHubProducer(_ => mock, retryPolicy);

            // Invoke an operation to force the producer to be lazily instantiated.  Otherwise,
            // construction does not happen.

            await producer.SendAsync(new[] { new EventData(new byte[] { 0x12, 0x22 }) }, new SendOptions(), default);

            Assert.That(mock.ConstructedWithPartition, Is.EqualTo(partition));

            EventHubRetryPolicy producerRetry = GetRetryPolicy(producer);

            Assert.That(producerRetry, Is.SameAs(retryPolicy), "The producer retry instance should match.");
        }
Example #25
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="AmqpProducer"/> class.
        /// </summary>
        ///
        /// <param name="eventHubName">The name of the Event Hub to which events will be published.</param>
        /// <param name="partitionId">The identifier of the Event Hub partition to which it is bound; if unbound, <c>null</c>.</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 AmqpProducer(string eventHubName,
                            string partitionId,
                            AmqpConnectionScope connectionScope,
                            AmqpMessageConverter messageConverter,
                            EventHubRetryPolicy retryPolicy)
        {
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(connectionScope, nameof(connectionScope));
            Argument.AssertNotNull(messageConverter, nameof(messageConverter));
            Argument.AssertNotNull(retryPolicy, nameof(retryPolicy));

            EventHubName     = eventHubName;
            PartitionId      = partitionId;
            RetryPolicy      = retryPolicy;
            ConnectionScope  = connectionScope;
            MessageConverter = messageConverter;
            SendLink         = new FaultTolerantAmqpObject <SendingAmqpLink>(timeout => CreateLinkAndEnsureProducerStateAsync(partitionId, timeout, CancellationToken.None), link => link.SafeClose());
        }
Example #26
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="TrackOneEventHubClient"/> class.
        /// </summary>
        ///
        /// <param name="host">The fully qualified host name for the Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
        /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls may be specified by the Event Hubs namespace or the requested Event Hub, depending on Azure configuration.</param>
        /// <param name="clientOptions">A set of options to apply when configuring the client.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="clientOptions" />.</param>
        /// <param name="eventHubClientFactory">A delegate that can be used for creation of the <see cref="TrackOne.EventHubClient" /> to which operations are delegated to.</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 TrackOneEventHubClient(string host,
                                      string eventHubName,
                                      TokenCredential credential,
                                      EventHubClientOptions clientOptions,
                                      EventHubRetryPolicy defaultRetryPolicy,
                                      Func <string, string, TokenCredential, EventHubClientOptions, Func <EventHubRetryPolicy>, TrackOne.EventHubClient> eventHubClientFactory)
        {
            Argument.AssertNotNullOrEmpty(host, nameof(host));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(credential, nameof(credential));
            Argument.AssertNotNull(clientOptions, nameof(clientOptions));
            Argument.AssertNotNull(defaultRetryPolicy, nameof(defaultRetryPolicy));

            EventHubName = eventHubName;

            _retryPolicy    = defaultRetryPolicy;
            _trackOneClient = new Lazy <TrackOne.EventHubClient>(() => eventHubClientFactory(host, eventHubName, credential, clientOptions, () => _retryPolicy), LazyThreadSafetyMode.ExecutionAndPublication);
        }
Example #27
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="AmqpEventHubClient"/> class.
        /// </summary>
        ///
        /// <param name="host">The fully qualified host name for the Event Hubs namespace.  This is likely to be similar to <c>{yournamespace}.servicebus.windows.net</c>.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to connect the client to.</param>
        /// <param name="credential">The Azure managed identity credential to use for authorization.  Access controls may be specified by the Event Hubs namespace or the requested Event Hub, depending on Azure configuration.</param>
        /// <param name="clientOptions">A set of options to apply when configuring the client.</param>
        /// <param name="defaultRetryPolicy">The default retry policy to use if no retry options were specified in the <paramref name="clientOptions" />.</param>
        /// <param name="connectionScope">The optional scope to use for AMQP connection management.  If <c>null</c>, a new scope will be created.</param>
        /// <param name="messageConverter">The optional converter to use for transforming AMQP message-related types.  If <c>null</c>, a new converter will be created.</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>
        ///
        protected AmqpEventHubClient(string host,
                                     string eventHubName,
                                     TokenCredential credential,
                                     EventHubClientOptions clientOptions,
                                     EventHubRetryPolicy defaultRetryPolicy,
                                     AmqpConnectionScope connectionScope,
                                     AmqpMessageConverter messageConverter)
        {
            Argument.AssertNotNullOrEmpty(host, nameof(host));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(credential, nameof(credential));
            Argument.AssertNotNull(clientOptions, nameof(clientOptions));
            Argument.AssertNotNull(defaultRetryPolicy, nameof(defaultRetryPolicy));

            try
            {
                EventHubsEventSource.Log.EventHubClientCreateStart(host, eventHubName);

                EventHubName     = eventHubName;
                Credential       = credential;
                MessageConverter = messageConverter ?? new AmqpMessageConverter();

                if (connectionScope == null)
                {
                    var endpointBuilder = new UriBuilder
                    {
                        Scheme = clientOptions.TransportType.GetUriScheme(),
                        Host   = host
                    };

                    connectionScope = new AmqpConnectionScope(endpointBuilder.Uri, eventHubName, credential, clientOptions.TransportType, clientOptions.Proxy);
                }

                ConnectionScope = connectionScope;
                ManagementLink  = new FaultTolerantAmqpObject <RequestResponseAmqpLink>(timeout => ConnectionScope.OpenManagementLinkAsync(timeout, CancellationToken.None), link => link.SafeClose());

                _retryPolicy = defaultRetryPolicy;
                _tryTimeout  = _retryPolicy.CalculateTryTimeout(0);
            }
            finally
            {
                EventHubsEventSource.Log.EventHubClientCreateComplete(host, eventHubName);
            }
        }
        /// <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));
        }
Example #29
0
        /// <summary>
        ///   Retrieves information about a specific partition for an Event Hub, including elements that describe the available
        ///   events in the partition event stream.
        /// </summary>
        ///
        /// <param name="partitionId">The unique identifier of a partition associated with the Event Hub.</param>
        /// <param name="retryPolicy">The retry policy to use as the basis for retrieving the information.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>The set of information for the requested partition under the Event Hub this client is associated with.</returns>
        ///
        public override async Task <PartitionProperties> GetPartitionPropertiesAsync(string partitionId,
                                                                                     EventHubRetryPolicy retryPolicy,
                                                                                     CancellationToken cancellationToken)
        {
            Argument.AssertNotClosed(_closed, nameof(AmqpClient));
            Argument.AssertNotNullOrEmpty(partitionId, nameof(partitionId));
            Argument.AssertNotNull(retryPolicy, nameof(retryPolicy));

            var failedAttemptCount = 0;
            var retryDelay         = default(TimeSpan?);
            var token = default(string);
            var link  = default(RequestResponseAmqpLink);

            var stopWatch = Stopwatch.StartNew();

            try
            {
                var tryTimeout = retryPolicy.CalculateTryTimeout(0);

                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        EventHubsEventSource.Log.GetPartitionPropertiesStart(EventHubName, partitionId);

                        // Create the request message and the management link.

                        token = await AquireAccessTokenAsync(cancellationToken).ConfigureAwait(false);

                        using AmqpMessage request = MessageConverter.CreatePartitionPropertiesRequest(EventHubName, partitionId, token);
                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        link = await ManagementLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout.CalculateRemaining(stopWatch.Elapsed))).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();

                        // Send the request and wait for the response.

                        using AmqpMessage response = await link.RequestAsync(request, tryTimeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false);

                        cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>();
                        stopWatch.Stop();

                        // Process the response.

                        AmqpError.ThrowIfErrorResponse(response, EventHubName);
                        return(MessageConverter.CreatePartitionPropertiesFromResponse(response));
                    }
                    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, mark the exception as active and break out of the loop.

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

                        if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested))
                        {
                            EventHubsEventSource.Log.GetPartitionPropertiesError(EventHubName, partitionId, ex.Message);
                            await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false);

                            tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount);
                            stopWatch.Reset();
                        }
                        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.GetPartitionPropertiesError(EventHubName, partitionId, ex.Message);
                throw;
            }
            finally
            {
                stopWatch.Stop();
                EventHubsEventSource.Log.GetPartitionPropertiesComplete(EventHubName, partitionId);
            }
        }
Example #30
0
 /// <summary>
 ///   Updates the active retry policy for the client.
 /// </summary>
 ///
 /// <param name="newRetryPolicy">The retry policy to set as active.</param>
 ///
 public abstract void UpdateRetryPolicy(EventHubRetryPolicy newRetryPolicy);