/// <summary>
        ///   Creates the AMQP link to be used for producer-related operations and ensures
        ///   that the corresponding state for the producer has been updated based on the link
        ///   configuration.
        /// </summary>
        ///
        /// <param name="partitionId">The identifier of the Event Hub partition to which it is bound; if unbound, <c>null</c>.</param>
        /// <param name="producerIdentifier">The identifier associated with the producer.</param>
        /// <param name="partitionOptions">The set of options, if any, that should be considered when initializing the producer.</param>
        /// <param name="timeout">The timeout to apply for creating the link.</param>
        /// <param name="cancellationToken">The cancellation token to consider when creating the link.</param>
        ///
        /// <returns>The AMQP link to use for producer-related operations.</returns>
        ///
        /// <remarks>
        ///   This method will modify class-level state, setting those attributes that depend on the AMQP
        ///   link configuration.  There exists a benign race condition in doing so, as there may be multiple
        ///   concurrent callers.  In this case, the attributes may be set multiple times but the resulting
        ///   value will be the same.
        /// </remarks>
        ///
        protected virtual async Task <SendingAmqpLink> CreateLinkAndEnsureProducerStateAsync(string partitionId,
                                                                                             string producerIdentifier,
                                                                                             PartitionPublishingOptions partitionOptions,
                                                                                             TimeSpan timeout,
                                                                                             CancellationToken cancellationToken)
        {
            var link             = default(SendingAmqpLink);
            var operationTimeout = RetryPolicy.CalculateTryTimeout(0);

            try
            {
                link = await ConnectionScope.OpenProducerLinkAsync(partitionId, ActiveFeatures, partitionOptions, operationTimeout, timeout, producerIdentifier, cancellationToken).ConfigureAwait(false);

                if (!MaximumMessageSize.HasValue)
                {
                    // This delay is necessary to prevent the link from causing issues for subsequent
                    // operations after creating a batch.  Without it, operations using the link consistently
                    // timeout.  The length of the delay does not appear significant, just the act of introducing
                    // an asynchronous delay.
                    //
                    // For consistency the value used by the legacy Event Hubs client has been brought forward and
                    // used here.

                    await Task.Delay(15, cancellationToken).ConfigureAwait(false);

                    MaximumMessageSize = (long)link.Settings.MaxMessageSize;
                }

                if (InitializedPartitionProperties == null)
                {
                    var producerGroup = link.ExtractSettingPropertyValueOrDefault(AmqpProperty.ProducerGroupId, default(long?));
                    var ownerLevel    = link.ExtractSettingPropertyValueOrDefault(AmqpProperty.ProducerOwnerLevel, default(short?));
                    var sequence      = link.ExtractSettingPropertyValueOrDefault(AmqpProperty.ProducerSequenceNumber, default(int?));

                    // Once the properties are initialized, clear the starting sequence number to ensure that the current
                    // sequence tracked by the service is used should the link need to be recreated; this avoids the need for
                    // the transport producer to have awareness of the sequence numbers of events being sent.

                    InitializedPartitionProperties          = new PartitionPublishingProperties(false, producerGroup, ownerLevel, sequence);
                    partitionOptions.StartingSequenceNumber = null;
                }
            }
            catch (Exception ex)
            {
                ExceptionDispatchInfo.Capture(ex.TranslateConnectionCloseDuringLinkCreationException(EventHubName)).Throw();
            }

            return(link);
        }
Example #2
0
        /// <summary>
        ///   Creates the AMQP link to be used for producer-related operations and ensures
        ///   that the corresponding state for the producer has been updated based on the link
        ///   configuration.
        /// </summary>
        ///
        /// <param name="partitionId">The identifier of the Event Hub partition to which it is bound; if unbound, <c>null</c>.</param>
        /// <param name="partitionOptions">The set of options, if any, that should be considered when initializing the producer.</param>
        /// <param name="timeout">The timeout to apply when creating the link.</param>
        /// <param name="cancellationToken">The cancellation token to consider when creating the link.</param>
        ///
        /// <returns>The AMQP link to use for producer-related operations.</returns>
        ///
        /// <remarks>
        ///   This method will modify class-level state, setting those attributes that depend on the AMQP
        ///   link configuration.  There exists a benign race condition in doing so, as there may be multiple
        ///   concurrent callers.  In this case, the attributes may be set multiple times but the resulting
        ///   value will be the same.
        /// </remarks>
        ///
        protected virtual async Task <SendingAmqpLink> CreateLinkAndEnsureProducerStateAsync(string partitionId,
                                                                                             PartitionPublishingOptions partitionOptions,
                                                                                             TimeSpan timeout,
                                                                                             CancellationToken cancellationToken)
        {
            var link = default(SendingAmqpLink);

            try
            {
                link = await ConnectionScope.OpenProducerLinkAsync(partitionId, timeout, cancellationToken).ConfigureAwait(false);

                if (!MaximumMessageSize.HasValue)
                {
                    // This delay is necessary to prevent the link from causing issues for subsequent
                    // operations after creating a batch.  Without it, operations using the link consistently
                    // timeout.  The length of the delay does not appear significant, just the act of introducing
                    // an asynchronous delay.
                    //
                    // For consistency the value used by the legacy Event Hubs client has been brought forward and
                    // used here.

                    await Task.Delay(15, cancellationToken).ConfigureAwait(false);

                    MaximumMessageSize = (long)link.Settings.MaxMessageSize;
                }

                if (InitializedPartitionProperties == null)
                {
                    if ((ActiveFeatures & TransportProducerFeatures.IdempotentPublishing) != 0)
                    {
                        throw new NotImplementedException(nameof(CreateLinkAndEnsureProducerStateAsync));
                    }
                    else
                    {
                        InitializedPartitionProperties = new PartitionPublishingProperties(false, null, null, null);
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionDispatchInfo.Capture(ex.TranslateConnectionCloseDuringLinkCreationException(EventHubName)).Throw();
            }

            return(link);
        }