Example #1
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));
        }
Example #2
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>
        /// <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 AmqpEventHubConsumer(string eventHubName,
                                    string consumerGroup,
                                    string partitionId,
                                    EventPosition eventPosition,
                                    EventHubConsumerOptions consumerOptions,
                                    AmqpConnectionScope connectionScope,
                                    AmqpMessageConverter messageConverter,
                                    EventHubRetryPolicy retryPolicy,
                                    LastEnqueuedEventProperties lastEnqueuedEventProperties) : base(lastEnqueuedEventProperties)
        {
            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;
            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 #3
0
        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);
            var 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.");

            var consumerRetry = GetRetryPolicy(consumer);

            Assert.That(consumerRetry, Is.SameAs(retryPolicy), "The consumer retry instance should match.");
        }
Example #4
0
        public async Task ReadPartitionTrackLastEnqueued()
        {
            await using var scope = await EventHubScope.CreateAsync(1);

            #region Snippet:EventHubs_Sample05_ReadPartitionTrackLastEnqueued

            var connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>";
            var eventHubName     = "<< NAME OF THE EVENT HUB >>";
            var consumerGroup    = EventHubConsumerClient.DefaultConsumerGroupName;
            /*@@*/
            /*@@*/ connectionString = EventHubsTestEnvironment.Instance.EventHubsConnectionString;
            /*@@*/ eventHubName     = scope.EventHubName;

            var consumer = new EventHubConsumerClient(
                consumerGroup,
                connectionString,
                eventHubName);

            try
            {
                using CancellationTokenSource cancellationSource = new CancellationTokenSource();
                cancellationSource.CancelAfter(TimeSpan.FromSeconds(30));

                string        firstPartition   = (await consumer.GetPartitionIdsAsync(cancellationSource.Token)).First();
                EventPosition startingPosition = EventPosition.Earliest;

                var options = new ReadEventOptions
                {
                    TrackLastEnqueuedEventProperties = true
                };

                await foreach (PartitionEvent partitionEvent in consumer.ReadEventsFromPartitionAsync(
                                   firstPartition,
                                   startingPosition,
                                   options,
                                   cancellationSource.Token))
                {
                    LastEnqueuedEventProperties properties =
                        partitionEvent.Partition.ReadLastEnqueuedEventProperties();

                    Debug.WriteLine($"Partition: { partitionEvent.Partition.PartitionId }");
                    Debug.WriteLine($"\tThe last sequence number is: { properties.SequenceNumber }");
                    Debug.WriteLine($"\tThe last offset is: { properties.Offset }");
                    Debug.WriteLine($"\tThe last enqueued time is: { properties.EnqueuedTime }, in UTC.");
                    Debug.WriteLine($"\tThe information was updated at: { properties.LastReceivedTime }, in UTC.");
                }
            }
            catch (TaskCanceledException)
            {
                // This is expected if the cancellation token is
                // signaled.
            }
            finally
            {
                await consumer.CloseAsync();
            }

            #endregion
        }
Example #5
0
        public void GetHashCodeReturnsDifferentValuesForDifferentMembers()
        {
            var now    = DateTimeOffset.UtcNow;
            var first  = new LastEnqueuedEventProperties(lastSequenceNumber: 333, lastOffset: 888, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now);
            var second = new LastEnqueuedEventProperties(lastSequenceNumber: 555, lastOffset: 777, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now);

            Assert.That(first.GetHashCode(), Is.Not.EqualTo(second.GetHashCode()));
        }
Example #6
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
                   ));
        }
Example #7
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);
        }
Example #8
0
        public void DifferentLastReceiveTimesAreNotEqual()
        {
            var now    = DateTimeOffset.UtcNow;
            var first  = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now);
            var second = new LastEnqueuedEventProperties(lastSequenceNumber: 123, lastOffset: 887, lastEnqueuedTime: DateTimeOffset.Parse("2015-10-27T12:00:00Z"), lastReceivedTime: now.AddHours(1));

            Assert.That(first.Equals((object)second), Is.False, "The default Equals comparison is incorrect.");
            Assert.That(first.Equals(second), Is.False, "The IEquatable comparison is incorrect.");
            Assert.That((first == second), Is.False, "The == operator comparison is incorrect.");
            Assert.That((first != second), Is.True, "The != operator comparison is incorrect.");
        }
Example #9
0
        public void ToStringReflectsTheState()
        {
            var offset        = 123;
            var sequence      = 778;
            var enqueued      = DateTimeOffset.Now.AddHours(1);
            var received      = DateTimeOffset.Now.AddHours(7);
            var properties    = new LastEnqueuedEventProperties(sequence, offset, enqueued, received);
            var toStringValue = properties.ToString();

            Assert.That(toStringValue, Contains.Substring($"[{ offset }]"), "The offset should be represented.");
            Assert.That(toStringValue, Contains.Substring($"[{ sequence }]"), "The sequence number should be represented.");
            Assert.That(toStringValue, Contains.Substring($"[{ enqueued }]"), "The enqueued time should be represented.");
            Assert.That(toStringValue, Contains.Substring($"[{ received }]"), "The received time should be represented.");
        }
        public async Task ReadLastEnqueuedEventPropertiesReadsPropertiesWhenThePartitionIsOwned()
        {
            using var processorCancellation = new CancellationTokenSource();
            using var cancellationSource    = new CancellationTokenSource();
            cancellationSource.CancelAfter(EventHubsTestEnvironment.Instance.TestExecutionTimeLimit);

            var partitionId         = "27";
            var partitionIds        = new[] { "0", partitionId };
            var ownedPartitions     = new List <string>();
            var lastEventProperties = new LastEnqueuedEventProperties(1234, 9876, DateTimeOffset.Parse("2015-10-27T00:00:00Z"), DateTimeOffset.Parse("2012-03-04T08:30:00Z"));
            var completionSource    = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            var options             = new EventProcessorOptions {
                LoadBalancingUpdateInterval = TimeSpan.FromMinutes(5), TrackLastEnqueuedEventProperties = true
            };
            var mockLoadBalancer = new Mock <PartitionLoadBalancer>();
            var mockConnection   = new Mock <EventHubConnection>();
            var mockProcessor    = new Mock <MinimalProcessorMock>(65, "consumerGroup", "namespace", "eventHub", Mock.Of <TokenCredential>(), options, mockLoadBalancer.Object)
            {
                CallBase = true
            };

            mockLoadBalancer
            .SetupGet(processor => processor.OwnedPartitionIds)
            .Returns(ownedPartitions);

            mockLoadBalancer
            .Setup(lb => lb.RunLoadBalancingAsync(partitionIds, It.IsAny <CancellationToken>()))
            .Callback(() =>
            {
                GetActivePartitionProcessors(mockProcessor.Object).TryAdd(
                    partitionId,
                    new EventProcessor <EventProcessorPartition> .PartitionProcessor(Task.Delay(Timeout.Infinite, processorCancellation.Token), new EventProcessorPartition {
                    PartitionId = partitionId
                }, () => lastEventProperties, processorCancellation)
                    );

                ownedPartitions.Add(partitionId);
                completionSource.TrySetResult(true);
            })
            .Returns(() => default);
Example #11
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="PartitionContext"/> class.
 /// </summary>
 ///
 /// <param name="partitionId">The identifier of the Event Hub partition this context is associated with.</param>
 /// <param name="lastEnqueuedEventProperties">The set of properties to be returned when <see cref="PartitionContext.ReadLastEnqueuedEventProperties" /> is invoked.</param>
 ///
 public static PartitionContext PartitionContext(string partitionId,
                                                 LastEnqueuedEventProperties lastEnqueuedEventProperties = default) =>
 new FactoryPartitionContext(partitionId, lastEnqueuedEventProperties);
Example #12
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="FactoryPartitionContext"/> class.
 /// </summary>
 ///
 /// <param name="partitionId">The identifier of the Event Hub partition this context is associated with.</param>
 /// <param name="lastEnqueuedEventProperties">The set of properties to be returned when <see cref="ReadLastEnqueuedEventProperties" /> is invoked.</param>
 ///
 internal FactoryPartitionContext(string partitionId,
                                  LastEnqueuedEventProperties lastEnqueuedEventProperties) : base(partitionId) =>
Example #13
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="Consumer.PartitionContext"/> class.
 /// </summary>
 ///
 /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace this context is associated with.</param>
 /// <param name="eventHubName">The name of the Event Hub partition this context is associated with.</param>
 /// <param name="consumerGroup">The name of the consumer group this context is associated with.</param>
 /// <param name="partitionId">The identifier of the Event Hub partition this context is associated with.</param>
 /// <param name="lastEnqueuedEventProperties">The set of properties to be returned when <see cref="PartitionContext.ReadLastEnqueuedEventProperties" /> is invoked.</param>
 ///
 public static PartitionContext PartitionContext(string fullyQualifiedNamespace,
                                                 string eventHubName,
                                                 string consumerGroup,
                                                 string partitionId,
                                                 LastEnqueuedEventProperties lastEnqueuedEventProperties = default) =>
 new FactoryPartitionContext(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId, lastEnqueuedEventProperties);
Example #14
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="FactoryPartitionContext"/> class.
 /// </summary>
 ///
 /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace this context is associated with.</param>
 /// <param name="eventHubName">The name of the Event Hub partition this context is associated with.</param>
 /// <param name="consumerGroup">The name of the consumer group this context is associated with.</param>
 /// <param name="partitionId">The identifier of the Event Hub partition this context is associated with.</param>
 /// <param name="lastEnqueuedEventProperties">The set of properties to be returned when <see cref="ReadLastEnqueuedEventProperties" /> is invoked.</param>
 ///
 internal FactoryPartitionContext(string fullyQualifiedNamespace,
                                  string eventHubName,
                                  string consumerGroup,
                                  string partitionId,
                                  LastEnqueuedEventProperties lastEnqueuedEventProperties) : base(fullyQualifiedNamespace, eventHubName, consumerGroup, partitionId) =>
Example #15
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="TransportEventHubConsumer"/> class.
 /// </summary>
 ///
 /// <param name="lastEnqueuedEventProperties">The set of properties for the last event enqueued in a partition.</param>
 ///
 protected TransportEventHubConsumer(LastEnqueuedEventProperties lastEnqueuedEventProperties = null)
 {
     LastEnqueuedEventInformation = lastEnqueuedEventProperties;
 }