예제 #1
0
        public void CloneProducesACopy()
        {
            var options = new EventHubBufferedProducerClientOptions
            {
                MaximumWaitTime = TimeSpan.FromSeconds(2),
                MaximumEventBufferLengthPerPartition = 3000,
                EnableIdempotentRetries            = true,
                MaximumConcurrentSends             = 9,
                MaximumConcurrentSendsPerPartition = 5,
                Identifier        = "Test-Options",
                ConnectionOptions = new EventHubConnectionOptions {
                    TransportType = EventHubsTransportType.AmqpWebSockets
                }
            };

            // Update the options without assigning a new instance.  This will ensure that the default custom type
            // that defines buffered publishing defaults is maintained.

            options.RetryOptions.TryTimeout = TimeSpan.FromMinutes(26);

            var clone = options.Clone();

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

            Assert.That(clone.Identifier, Is.EqualTo(options.Identifier), "The identifier should match.");
            Assert.That(clone.EnableIdempotentRetries, Is.EqualTo(options.EnableIdempotentRetries), "The flag to enable idempotent publishing should have been copied.");
            Assert.That(clone.ConnectionOptions.TransportType, Is.EqualTo(options.ConnectionOptions.TransportType), "The connection options of the clone should copy properties.");
            Assert.That(clone.ConnectionOptions, Is.Not.SameAs(options.ConnectionOptions), "The connection options of the clone should be a copy, not the same instance.");
            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.");
            Assert.That(clone.MaximumConcurrentSends, Is.EqualTo(options.MaximumConcurrentSends), "The number of concurrent total sends should have been copied.");
            Assert.That(clone.MaximumConcurrentSendsPerPartition, Is.EqualTo(options.MaximumConcurrentSendsPerPartition), "The number of concurrent sends to a partition should have been copied.");
            Assert.That(clone.MaximumWaitTime, Is.EqualTo(options.MaximumWaitTime), "The maximum wait time should have been copied.");
            Assert.That(clone.MaximumEventBufferLengthPerPartition, Is.EqualTo(options.MaximumEventBufferLengthPerPartition), "The maximum event buffer length should have been copied.");
        }
예제 #2
0
        public async Task BufferedConfiguration()
        {
            await using var scope = await EventHubScope.CreateAsync(1);

            #region Snippet:EventHubs_Sample04_BufferedConfiguration

#if SNIPPET
            var connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>";
            var eventHubName     = "<< NAME OF THE EVENT HUB >>";
#else
            var connectionString = EventHubsTestEnvironment.Instance.EventHubsConnectionString;
            var eventHubName     = scope.EventHubName;
#endif

            var options = new EventHubBufferedProducerClientOptions
            {
                MaximumWaitTime                      = TimeSpan.FromSeconds(1),
                MaximumConcurrentSends               = 5,
                MaximumConcurrentSendsPerPartition   = 1,
                MaximumEventBufferLengthPerPartition = 5000,
                EnableIdempotentRetries              = true
            };

            var producer = new EventHubBufferedProducerClient(connectionString, eventHubName, options);

            // The failure handler is required and invoked after all allowable
            // retries were applied.

            producer.SendEventBatchFailedAsync += args =>
            {
                Debug.WriteLine($"Publishing failed for { args.EventBatch.Count } events.  Error: '{ args.Exception.Message }'");
                return(Task.CompletedTask);
            };

            // The success handler is optional.

            producer.SendEventBatchSucceededAsync += args =>
            {
                Debug.WriteLine($"{ args.EventBatch.Count } events were published to partition: '{ args.PartitionId }.");
                return(Task.CompletedTask);
            };

            try
            {
                for (var index = 0; index < 5; ++index)
                {
                    var eventData = new EventData($"Event #{ index }");
                    await producer.EnqueueEventAsync(eventData);
                }
            }
            finally
            {
                // Closing the producer will flush any
                // enqueued events that have not been published.

                await producer.CloseAsync();
            }

            #endregion
        }
예제 #3
0
        public void ToEventHubProducerClientOptionsProducesAValidInstance()
        {
            var options = new EventHubBufferedProducerClientOptions
            {
                MaximumWaitTime = TimeSpan.FromSeconds(2),
                MaximumEventBufferLengthPerPartition = 3000,
                EnableIdempotentRetries            = true,
                MaximumConcurrentSends             = 9,
                MaximumConcurrentSendsPerPartition = 5,
                Identifier        = "Test-Options",
                ConnectionOptions = new EventHubConnectionOptions {
                    TransportType = EventHubsTransportType.AmqpWebSockets
                },
                RetryOptions = new EventHubsRetryOptions {
                    TryTimeout = TimeSpan.FromMinutes(36)
                }
            };

            var transformedOptions = options.ToEventHubProducerClientOptions();

            Assert.That(transformedOptions.Identifier, Is.EqualTo(options.Identifier), "The identifier should match.");
            Assert.That(transformedOptions.ConnectionOptions.TransportType, Is.EqualTo(options.ConnectionOptions.TransportType), "The connection options of the clone should copy properties.");
            Assert.That(transformedOptions.RetryOptions.IsEquivalentTo(options.RetryOptions), Is.True, "The retry options of the clone should be considered equal.");
            Assert.That(transformedOptions.EnableIdempotentPartitions, Is.EqualTo(options.EnableIdempotentRetries), "Idempotent partitions should be the same as idempotent retries.");
        }
        /// <summary>
        ///   Performs the tasks needed to initialize and set up the environment for the test scenario.
        ///   This setup will take place once for each instance, running after the global setup has
        ///   completed.
        /// </summary>
        ///
        public override async Task SetupAsync()
        {
            await base.SetupAsync();

            Scope = await EventHubScope.CreateAsync(Options.PartitionCount).ConfigureAwait(false);

            var producerOptions = new EventHubBufferedProducerClientOptions
            {
                MaximumWaitTime = (Options.MaximumWaitTimeMilliseconds.HasValue) ? TimeSpan.FromMilliseconds(Options.MaximumWaitTimeMilliseconds.Value) : null,
                MaximumEventBufferLengthPerPartition = Options.MaximumBufferLength,
                MaximumConcurrentSendsPerPartition   = Options.MaximumConcurrentSendsPerPartition
            };

            if (Options.MaximumConcurrentSends.HasValue)
            {
                producerOptions.MaximumConcurrentSends = Options.MaximumConcurrentSends.Value;
            }

            _producer = new EventHubBufferedProducerClient(TestEnvironment.EventHubsConnectionString, Scope.EventHubName, producerOptions);

            // Create the handlers that call into the performance test infrastructure to
            // report when errors and events are observed.

            _producer.SendEventBatchFailedAsync += args =>
            {
                // Do not flag cancellation as a failure; it is expected for in-flight batches when
                // the test run is cleaning up.

                if (!typeof(OperationCanceledException).IsAssignableFrom(args.Exception.GetType()))
                {
                    ErrorRaised(args.Exception);
                }

                return(Task.CompletedTask);
            };

            _producer.SendEventBatchSucceededAsync += args =>
            {
                for (var index = 0; index < args.EventBatch.Count; ++index)
                {
                    EventRaised();
                }

                return(Task.CompletedTask);
            };

            // Read the available partitions and buffer a single events to establish the
            // connection and link and start reading the buffers.

            Partitions = await _producer.GetPartitionIdsAsync().ConfigureAwait(false);

            await _producer.EnqueueEventsAsync(EventGenerator.CreateEvents(1)).ConfigureAwait(false);

            // Start the background publishing task.

            _backgroundCancellationSource = new CancellationTokenSource();
            _backgroundBufferingTask      = EnqueueEvents(_backgroundCancellationSource.Token);
        }
예제 #5
0
        public void RetryOptionsAreOptimizedForBufferedPublishing()
        {
            var standardRetryOptions = new EventHubsRetryOptions();
            var options = new EventHubBufferedProducerClientOptions();

            Assert.That(options.RetryOptions, Is.Not.Null, "The retry options should not be null.");
            Assert.That(options.RetryOptions.MaximumRetries, Is.GreaterThan(standardRetryOptions.MaximumRetries), "The buffered retry options should allow for more retries than the standard.");
            Assert.That(options.RetryOptions.TryTimeout, Is.GreaterThan(standardRetryOptions.TryTimeout), "The buffered retry options should allow for a longer try timeout than the standard.");
        }
        public void ConnectionConstructorSetsDelegatedProperties()
        {
            var expectedIdentifier = "Test-Identifier";
            var expectedNamespace  = "testns.namespace.com";
            var expectedEventHub   = "testHub";
            var options            = new EventHubBufferedProducerClientOptions {
                Identifier = expectedIdentifier
            };
            var connection = new EventHubConnection(expectedNamespace, expectedEventHub, Mock.Of <TokenCredential>());
            var producer   = new EventHubBufferedProducerClient(connection, options);

            Assert.That(producer.Identifier, Is.EqualTo(expectedIdentifier), "The identifier should have been initialized.");
            Assert.That(producer.FullyQualifiedNamespace, Is.EqualTo(expectedNamespace), "The fully qualified namespace should have been initialized.");
            Assert.That(producer.EventHubName, Is.EqualTo(expectedEventHub), "The event hub name should have been initialized.");
        }
        public void SasCredentialConstructorSetsDelegatedProperties()
        {
            var expectedIdentifier = "Test-Identifier";
            var expectedNamespace  = "testns.namespace.com";
            var expectedEventHub   = "testHub";
            var credential         = new AzureSasCredential(new SharedAccessSignature("sb://this.is.Fake/blah", "key", "value").Value);
            var options            = new EventHubBufferedProducerClientOptions {
                Identifier = expectedIdentifier
            };
            var producer = new EventHubBufferedProducerClient(expectedNamespace, expectedEventHub, credential, options);

            Assert.That(producer.Identifier, Is.EqualTo(expectedIdentifier), "The identifier should have been initialized.");
            Assert.That(producer.FullyQualifiedNamespace, Is.EqualTo(expectedNamespace), "The fully qualified namespace should have been initialized.");
            Assert.That(producer.EventHubName, Is.EqualTo(expectedEventHub), "The event hub name should have been initialized.");
        }
        public void ConnectionStringAndEventHubConstructorSetsDelegatedProperties()
        {
            var expectedIdentifier = "Test-Identifier";
            var expectedNamespace  = "testns.namespace.com";
            var expectedEventHub   = "testHub";
            var connectionString   = $"Endpoint=sb://{ expectedNamespace };SharedAccessKeyName=ABC;SharedAccessKey=123";
            var options            = new EventHubBufferedProducerClientOptions {
                Identifier = expectedIdentifier
            };
            var producer = new EventHubBufferedProducerClient(connectionString, expectedEventHub, options);

            Assert.That(producer.Identifier, Is.EqualTo(expectedIdentifier), "The identifier should have been initialized.");
            Assert.That(producer.FullyQualifiedNamespace, Is.EqualTo(expectedNamespace), "The fully qualified namespace should have been initialized.");
            Assert.That(producer.EventHubName, Is.EqualTo(expectedEventHub), "The event hub name should have been initialized.");
        }
예제 #9
0
        public async Task Start(CancellationToken cancellationToken)
        {
            var enqueueTasks = new List <Task>();

            while (!cancellationToken.IsCancellationRequested)
            {
                using var backgroundCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

                // Create the buffered producer client and register the success and failure handlers

                var options = new EventHubBufferedProducerClientOptions
                {
                    RetryOptions = new EventHubsRetryOptions
                    {
                        TryTimeout     = _testConfiguration.SendTimeout,
                        MaximumRetries = 15
                    },
                    MaximumWaitTime = _testConfiguration.MaxWaitTime
                };
                var producer = new EventHubBufferedProducerClient(_connectionString, _eventHubName, options);

                try
                {
                    producer.SendEventBatchSucceededAsync += args =>
                    {
                        var numEvents = args.EventBatch.Count;

                        _metrics.Client.GetMetric(_metrics.SuccessfullySentFromQueue, "PartitionId").TrackValue(numEvents, args.PartitionId);
                        _metrics.Client.GetMetric(_metrics.BatchesPublished).TrackValue(1);

                        return(Task.CompletedTask);
                    };

                    producer.SendEventBatchFailedAsync += args =>
                    {
                        var numEvents = args.EventBatch.Count;

                        _metrics.Client.GetMetric(_metrics.EventsNotSentAfterEnqueue, "PartitionId").TrackValue(numEvents, args.PartitionId);
                        _metrics.Client.TrackException(args.Exception);

                        return(Task.CompletedTask);
                    };

                    // Start concurrent enqueuing tasks

                    if (_testConfiguration.ConcurrentSends > 1)
                    {
                        for (var index = 0; index < _testConfiguration.ConcurrentSends - 1; ++index)
                        {
                            enqueueTasks.Add(Task.Run(async() =>
                            {
                                while (!cancellationToken.IsCancellationRequested)
                                {
                                    await PerformEnqueue(producer, cancellationToken).ConfigureAwait(false);

                                    if ((_testConfiguration.ProducerPublishingDelay.HasValue) && (_testConfiguration.ProducerPublishingDelay.Value > TimeSpan.Zero))
                                    {
                                        await Task.Delay(_testConfiguration.ProducerPublishingDelay.Value, backgroundCancellationSource.Token).ConfigureAwait(false);
                                    }
                                }
                            }));
                        }
                    }
                    // Perform one of the sends in the foreground, which will allow easier detection of a
                    // processor-level issue.

                    while (!cancellationToken.IsCancellationRequested)
                    {
                        try
                        {
                            await PerformEnqueue(producer, cancellationToken).ConfigureAwait(false);

                            if ((_testConfiguration.ProducerPublishingDelay.HasValue) && (_testConfiguration.ProducerPublishingDelay.Value > TimeSpan.Zero))
                            {
                                await Task.Delay(_testConfiguration.ProducerPublishingDelay.Value, cancellationToken).ConfigureAwait(false);
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            backgroundCancellationSource.Cancel();
                            await Task.WhenAll(enqueueTasks).ConfigureAwait(false);
                        }
                    }
                }
                catch (TaskCanceledException)
                {
                    // No action needed, the cancellation token has been cancelled.
                }
                catch (Exception ex) when
                    (ex is OutOfMemoryException ||
                    ex is StackOverflowException ||
                    ex is ThreadAbortException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    // If this catch is hit, it means the producer has restarted, collect metrics.

                    _metrics.Client.GetMetric(_metrics.ProducerRestarted).TrackValue(1);
                    _metrics.Client.TrackException(ex);
                }
                finally
                {
                    await producer.CloseAsync(false);
                }
            }
        }
예제 #10
0
        public void MaximumWaitTimeNegativeOutOfRange()
        {
            var options = new EventHubBufferedProducerClientOptions();

            Assert.That(() => options.MaximumWaitTime = TimeSpan.FromMilliseconds(-1), Throws.InstanceOf <ArgumentOutOfRangeException>());
        }
예제 #11
0
        public void MaximumConcurrentSendsPerPartitionNegativeOutOfRange()
        {
            var options = new EventHubBufferedProducerClientOptions();

            Assert.That(() => options.MaximumConcurrentSendsPerPartition = -1, Throws.InstanceOf <ArgumentOutOfRangeException>());
        }
예제 #12
0
        public void MaximumEventBufferLengthPerPartitionSendsZeroOutOfRange()
        {
            var options = new EventHubBufferedProducerClientOptions();

            Assert.That(() => options.MaximumEventBufferLengthPerPartition = 0, Throws.InstanceOf <ArgumentOutOfRangeException>());
        }