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."); }
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 }
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); }
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."); }
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); } } }
public void MaximumWaitTimeNegativeOutOfRange() { var options = new EventHubBufferedProducerClientOptions(); Assert.That(() => options.MaximumWaitTime = TimeSpan.FromMilliseconds(-1), Throws.InstanceOf <ArgumentOutOfRangeException>()); }
public void MaximumConcurrentSendsPerPartitionNegativeOutOfRange() { var options = new EventHubBufferedProducerClientOptions(); Assert.That(() => options.MaximumConcurrentSendsPerPartition = -1, Throws.InstanceOf <ArgumentOutOfRangeException>()); }
public void MaximumEventBufferLengthPerPartitionSendsZeroOutOfRange() { var options = new EventHubBufferedProducerClientOptions(); Assert.That(() => options.MaximumEventBufferLengthPerPartition = 0, Throws.InstanceOf <ArgumentOutOfRangeException>()); }