/// <summary> /// Sends a set of events to the associated Event Hub using a batched approach. /// </summary> /// /// <param name="eventBatch">The set of event data to send. A batch may be created using <see cref="CreateBatchAsync(CancellationToken)" />.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken" /> instance to signal the request to cancel the operation.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// /// <seealso cref="SendAsync(EventData, CancellationToken)" /> /// <seealso cref="SendAsync(EventData, SendEventOptions, CancellationToken)" /> /// <seealso cref="SendAsync(IEnumerable{EventData}, CancellationToken)" /> /// <seealso cref="SendAsync(IEnumerable{EventData}, SendEventOptions, CancellationToken)" /> /// <seealso cref="CreateBatchAsync(CancellationToken)" /> /// public virtual async Task SendAsync(EventDataBatch eventBatch, CancellationToken cancellationToken = default) { Argument.AssertNotNull(eventBatch, nameof(eventBatch)); AssertSinglePartitionReference(eventBatch.SendOptions.PartitionId, eventBatch.SendOptions.PartitionKey); int attempts = 0; using DiagnosticScope scope = CreateDiagnosticScope(eventBatch.GetEventDiagnosticIdentifiers()); var pooledProducer = PartitionProducerPool.GetPooledProducer(eventBatch.SendOptions.PartitionId, PartitionProducerLifespan); while (!cancellationToken.IsCancellationRequested) { try { await using var _ = pooledProducer.ConfigureAwait(false); eventBatch.Lock(); await pooledProducer.TransportProducer.SendAsync(eventBatch, cancellationToken).ConfigureAwait(false); return; } catch (EventHubsException eventHubException) when(eventHubException.Reason == EventHubsException.FailureReason.ClientClosed && ShouldRecreateProducer(pooledProducer.TransportProducer, eventBatch.SendOptions.PartitionId)) { if (++attempts >= MaximumCreateProducerAttempts) { scope.Failed(eventHubException); throw; } pooledProducer = PartitionProducerPool.GetPooledProducer(eventBatch.SendOptions.PartitionId, PartitionProducerLifespan); } catch (Exception ex) { scope.Failed(ex); throw; } finally { eventBatch.Unlock(); } } throw new TaskCanceledException(); }
/// <summary> /// Sends a set of events to the associated Event Hub using a batched approach. /// </summary> /// /// <param name="eventBatch">The set of event data to send. A batch may be created using <see cref="CreateBatchAsync(CancellationToken)" />.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// /// <seealso cref="SendAsync(EventData, CancellationToken)" /> /// <seealso cref="SendAsync(EventData, SendEventOptions, CancellationToken)" /> /// <seealso cref="SendAsync(IEnumerable{EventData}, CancellationToken)" /> /// <seealso cref="SendAsync(IEnumerable{EventData}, SendEventOptions, CancellationToken)" /> /// <seealso cref="CreateBatchAsync(CancellationToken)" /> /// public virtual async Task SendAsync(EventDataBatch eventBatch, CancellationToken cancellationToken = default) { Argument.AssertNotNull(eventBatch, nameof(eventBatch)); AssertSinglePartitionReference(eventBatch.SendOptions.PartitionId, eventBatch.SendOptions.PartitionKey); // Determine the transport producer to delegate the send operation to. Because sending to a specific // partition requires a dedicated client, use (or create) that client if a partition was specified. Otherwise // the default gateway producer can be used to request automatic routing from the Event Hubs service gateway. TransportProducer activeProducer; if (string.IsNullOrEmpty(eventBatch.SendOptions.PartitionId)) { activeProducer = EventHubProducer; } else { // This assertion is intended as an additional check, not as a guarantee. There still exists a benign // race condition where a transport producer may be created after the client has been closed; in this case // the transport producer will be force-closed with the associated connection or, worst case, will close once // its idle timeout period elapses. Argument.AssertNotClosed(IsClosed, nameof(EventHubProducerClient)); activeProducer = PartitionProducers.GetOrAdd(eventBatch.SendOptions.PartitionId, id => Connection.CreateTransportProducer(id, RetryPolicy)); } using DiagnosticScope scope = CreateDiagnosticScope(eventBatch.GetEventDiagnosticIdentifiers()); try { eventBatch.Lock(); await activeProducer.SendAsync(eventBatch, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { scope.Failed(ex); throw; } finally { eventBatch.Unlock(); } }