/// <summary>
 ///   Sends an event to the associated Event Hub using a batched approach.  If the size of the event exceeds the
 ///   maximum size of a single batch, an exception will be triggered and the send will fail.
 /// </summary>
 ///
 /// <param name="eventData">The event data to send.</param>
 /// <param name="options">The set of options to consider when sending this batch.</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(IEnumerable{EventData}, CancellationToken)" />
 /// <seealso cref="SendAsync(IEnumerable{EventData}, SendEventOptions, CancellationToken)" />
 /// <seealso cref="SendAsync(EventDataBatch, CancellationToken)" />
 ///
 internal virtual async Task SendAsync(EventData eventData,
                                       SendEventOptions options,
                                       CancellationToken cancellationToken = default)
 {
     Argument.AssertNotNull(eventData, nameof(eventData));
     await SendAsync(new[] { eventData }, options, cancellationToken).ConfigureAwait(false);
 }
 /// <summary>
 ///   Sends an event to the associated Event Hub using a batched approach.  If the size of the event exceeds the
 ///   maximum size of a single batch, an exception will be triggered and the send will fail.
 /// </summary>
 ///
 /// <param name="eventData">The event data to send.</param>
 /// <param name="options">The set of options to consider when sending this batch.</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(IEnumerable{EventData}, CancellationToken)" />
 /// <seealso cref="SendAsync(IEnumerable{EventData}, SendEventOptions, CancellationToken)" />
 /// <seealso cref="SendAsync(EventDataBatch, CancellationToken)" />
 ///
 internal virtual Task SendAsync(EventData eventData,
                                 SendEventOptions options,
                                 CancellationToken cancellationToken = default)
 {
     Argument.AssertNotNull(eventData, nameof(eventData));
     return(SendAsync(new[] { eventData }, options, cancellationToken));
 }
Esempio n. 3
0
            public Task SendAsync(IEnumerable <EventData> eventDataBatch, string partitionKey, CancellationToken cancellationToken)
            {
                if (_client == null)
                {
                    throw new InvalidOperationException("EventHubClient has not been initialized");
                }

                Azure.Messaging.EventHubs.Producer.SendEventOptions sendEventOptions = null;
                if (!string.IsNullOrEmpty(partitionKey))
                {
                    if (!_partitionKeys.TryGetValue(partitionKey, out sendEventOptions))
                    {
                        sendEventOptions = new Azure.Messaging.EventHubs.Producer.SendEventOptions()
                        {
                            PartitionKey = partitionKey
                        };
                        _partitionKeys.TryAdd(partitionKey, sendEventOptions);
                    }
                }

                if (sendEventOptions != null)
                {
                    return(_client.SendAsync(eventDataBatch, sendEventOptions, cancellationToken));
                }
                else
                {
                    return(_client.SendAsync(eventDataBatch, cancellationToken));
                }
            }
        /// <summary>
        ///   Sends a set of events to the associated Event Hub using a batched approach.  If the size of events exceed the
        ///   maximum size of a single batch, an exception will be triggered and the send will fail.
        /// </summary>
        ///
        /// <param name="events">The set of event data to send.</param>
        /// <param name="options">The set of options to consider when sending this batch.</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(EventDataBatch, CancellationToken)" />
        ///
        internal virtual async Task SendAsync(IEnumerable <EventData> events,
                                              SendEventOptions options,
                                              CancellationToken cancellationToken = default)
        {
            options ??= DefaultSendOptions;

            Argument.AssertNotNull(events, nameof(events));
            AssertSinglePartitionReference(options.PartitionId, options.PartitionKey);

            int attempts = 0;

            events = (events as IList <EventData>) ?? events.ToList();
            InstrumentMessages(events);

            var diagnosticIdentifiers = new List <string>();

            foreach (var eventData in events)
            {
                if (EventDataInstrumentation.TryExtractDiagnosticId(eventData, out var identifier))
                {
                    diagnosticIdentifiers.Add(identifier);
                }
            }

            using DiagnosticScope scope = CreateDiagnosticScope(diagnosticIdentifiers);

            var pooledProducer = PartitionProducerPool.GetPooledProducer(options.PartitionId, PartitionProducerLifespan);

            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    await using var _ = pooledProducer.ConfigureAwait(false);

                    await pooledProducer.TransportProducer.SendAsync(events, options, cancellationToken).ConfigureAwait(false);

                    return;
                }
                catch (EventHubsException eventHubException)
                    when(eventHubException.Reason == EventHubsException.FailureReason.ClientClosed && ShouldRecreateProducer(pooledProducer.TransportProducer, options.PartitionId))
                    {
                        if (++attempts >= MaximumCreateProducerAttempts)
                        {
                            scope.Failed(eventHubException);
                            throw;
                        }

                        pooledProducer = PartitionProducerPool.GetPooledProducer(options.PartitionId, PartitionProducerLifespan);
                    }
                catch (Exception ex)
                {
                    scope.Failed(ex);
                    throw;
                }
            }

            throw new TaskCanceledException();
        }
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</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>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                SendEventOptions sendOptions)
        {
            Argument.AssertNotNull(transportBatch, nameof(transportBatch));
            Argument.AssertNotNull(sendOptions, nameof(sendOptions));

            InnerBatch  = transportBatch;
            SendOptions = sendOptions;
        }
        /// <summary>
        ///   Sends a set of events to the associated Event Hub using a batched approach.  If the size of events exceed the
        ///   maximum size of a single batch, an exception will be triggered and the send will fail.
        /// </summary>
        ///
        /// <param name="events">The set of event data to send.</param>
        /// <param name="options">The set of options to consider when sending this batch.</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(EventDataBatch, CancellationToken)" />
        ///
        internal virtual async Task SendAsync(IEnumerable <EventData> events,
                                              SendEventOptions options,
                                              CancellationToken cancellationToken = default)
        {
            options ??= DefaultSendOptions;

            Argument.AssertNotNull(events, nameof(events));
            AssertSinglePartitionReference(options.PartitionId, options.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(options.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(options.PartitionId, id => Connection.CreateTransportProducer(id, RetryPolicy));
            }

            events = (events as IList <EventData>) ?? events.ToList();
            InstrumentMessages(events);

            var diagnosticIdentifiers = new List <string>();

            foreach (var eventData in events)
            {
                if (EventDataInstrumentation.TryExtractDiagnosticId(eventData, out var identifier))
                {
                    diagnosticIdentifiers.Add(identifier);
                }
            }

            using DiagnosticScope scope = CreateDiagnosticScope(diagnosticIdentifiers);

            try
            {
                await activeProducer.SendAsync(events, options, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                scope.Failed(ex);
                throw;
            }
        }
Esempio n. 7
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace to use for instrumentation.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to associate the events with during instrumentation.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</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>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                string fullyQualifiedNamespace,
                                string eventHubName,
                                SendEventOptions sendOptions)
        {
            Argument.AssertNotNull(transportBatch, nameof(transportBatch));
            Argument.AssertNotNullOrEmpty(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(sendOptions, nameof(sendOptions));

            InnerBatch = transportBatch;
            FullyQualifiedNamespace = fullyQualifiedNamespace;
            EventHubName            = eventHubName;
            SendOptions             = sendOptions;
        }