/// <summary> /// Initializes a new instance of the <see cref="AmqpMessageBatch"/> class. /// </summary> /// /// <param name="options">The set of options to apply to the batch.</param> /// public AmqpMessageBatch(CreateMessageBatchOptions options) { Argument.AssertNotNull(options, nameof(options)); Argument.AssertNotNull(options.MaxSizeInBytes, nameof(options.MaxSizeInBytes)); Options = options; MaxSizeInBytes = options.MaxSizeInBytes.Value; // Initialize the size by reserving space for the batch envelope. using AmqpMessage envelope = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(Enumerable.Empty <ServiceBusMessage>()); _reservedSize = envelope.SerializedMessageSize; _sizeBytes = _reservedSize; }
/// <summary> /// Sends a set of messages to the associated Queue/Topic using a batched approach. /// </summary> /// /// <param name="messageBatch">The set of messages to send.</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> /// public override async Task SendBatchAsync( ServiceBusMessageBatch messageBatch, CancellationToken cancellationToken) { AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>()); await _retryPolicy.RunOperation(async (timeout) => await SendBatchInternalAsync( messageFactory, timeout, cancellationToken).ConfigureAwait(false), _connectionScope, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Sends a message to the associated Service Bus entity. /// </summary> /// /// <param name="message">A message to send.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// public override async Task SendAsync( ServiceBusMessage message, CancellationToken cancellationToken) { Argument.AssertNotNull(message, nameof(message)); Argument.AssertNotClosed(_closed, nameof(AmqpSender)); AmqpMessage messageFactory() => AmqpMessageConverter.SBMessageToAmqpMessage(message); await _retryPolicy.RunOperation(async (timeout) => await SendBatchInternalAsync( messageFactory, timeout, cancellationToken).ConfigureAwait(false), _connectionScope, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Attempts to add a message to the batch, ensuring that the size /// of the batch does not exceed its maximum. /// </summary> /// /// <param name="message">The message to attempt to add to the batch.</param> /// /// <returns><c>true</c> if the message was added; otherwise, <c>false</c>.</returns> /// public override bool TryAddMessage(ServiceBusMessage message) { Argument.AssertNotNull(message, nameof(message)); Argument.AssertNotDisposed(_disposed, nameof(ServiceBusMessageBatch)); AmqpMessage amqpMessage = null; try { if (BatchMessages.Count == 0) { // Initialize the size by reserving space for the batch envelope taking into account the properties from the first // message which will be used to populate properties on the batch envelope. amqpMessage = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(message, forceBatch: true); } else { amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message); } // Calculate the size for the message, based on the AMQP message size and accounting for a // bit of reserved overhead size. var size = _sizeBytes + amqpMessage.SerializedMessageSize + (amqpMessage.SerializedMessageSize <= MaximumBytesSmallMessage ? OverheadBytesSmallMessage : OverheadBytesLargeMessage); if (size > MaxSizeInBytes) { return(false); } _sizeBytes = size; BatchMessages.Add(message); return(true); } finally { amqpMessage?.Dispose(); } }
/// <summary> /// Sends a set of messages to the associated Queue/Topic using a batched approach. /// </summary> /// /// <param name="messageBatch"></param> /// <param name="timeout"></param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// internal virtual async Task SendBatchInternalAsync( ServiceBusMessageBatch messageBatch, TimeSpan timeout, CancellationToken cancellationToken) { var stopWatch = Stopwatch.StartNew(); AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>()); using (AmqpMessage batchMessage = messageFactory()) { //ServiceBusEventSource.Log.SendStart(Entityname, messageHash); string messageHash = batchMessage.GetHashCode().ToString(); SendingAmqpLink link = await _sendLink.GetOrCreateAsync(UseMinimum(_connectionScope.SessionTimeout, timeout)).ConfigureAwait(false); // Validate that the message is not too large to send. This is done after the link is created to ensure // that the maximum message size is known, as it is dictated by the service using the link. if (batchMessage.SerializedMessageSize > MaximumMessageSize) { throw new ServiceBusException(string.Format(Resources1.MessageSizeExceeded, messageHash, batchMessage.SerializedMessageSize, MaximumMessageSize, _entityName), ServiceBusException.FailureReason.MessageSizeExceeded); } // Attempt to send the message batch. var deliveryTag = new ArraySegment <byte>(BitConverter.GetBytes(Interlocked.Increment(ref _deliveryCount))); var outcome = await link.SendMessageAsync(batchMessage, deliveryTag, AmqpConstants.NullBinary, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpError.CreateExceptionForError((outcome as Rejected)?.Error, _entityName); } //ServiceBusEventSource.Log.SendStop(Entityname, messageHash); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); } }
/// <summary> /// Get all rules associated with the subscription. /// </summary> /// /// <param name="timeout">The per-try timeout specified in the RetryOptions.</param> /// /// <returns>Returns a list of rules description</returns> private async Task <IList <RuleProperties> > GetRulesInternalAsync(TimeSpan timeout) { var amqpRequestMessage = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.EnumerateRulesOperation, timeout, null); amqpRequestMessage.Map[ManagementConstants.Properties.Top] = int.MaxValue; amqpRequestMessage.Map[ManagementConstants.Properties.Skip] = 0; var response = await ManagementUtilities.ExecuteRequestResponseAsync( _connectionScope, _managementLink, amqpRequestMessage, null, timeout).ConfigureAwait(false); var ruleDescriptions = new List <RuleProperties>(); if (response.StatusCode == AmqpResponseStatusCode.OK) { var ruleList = response.GetListValue <AmqpMap>(ManagementConstants.Properties.Rules); foreach (var entry in ruleList) { var amqpRule = (AmqpRuleDescriptionCodec)entry[ManagementConstants.Properties.RuleDescription]; var ruleDescription = AmqpMessageConverter.GetRuleDescription(amqpRule); ruleDescriptions.Add(ruleDescription); } } else if (response.StatusCode == AmqpResponseStatusCode.NoContent) { // Do nothing. Return empty list; } else { throw response.ToMessagingContractException(); } return(ruleDescriptions); }
/// <summary> /// Adds a rule to the current subscription to filter the messages reaching from topic to the subscription. /// </summary> /// /// <param name="description">The rule description that provides the rule to add.</param> /// <param name="timeout">The per-try timeout specified in the RetryOptions.</param> /// /// <returns>A task instance that represents the asynchronous add rule operation.</returns> private async Task AddRuleInternalAsync( RuleProperties description, TimeSpan timeout) { // Create an AmqpRequest Message to add rule var amqpRequestMessage = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.AddRuleOperation, timeout, null); amqpRequestMessage.Map[ManagementConstants.Properties.RuleName] = description.Name; amqpRequestMessage.Map[ManagementConstants.Properties.RuleDescription] = AmqpMessageConverter.GetRuleDescriptionMap(description); AmqpResponseMessage response = await ManagementUtilities.ExecuteRequestResponseAsync( _connectionScope, _managementLink, amqpRequestMessage, timeout).ConfigureAwait(false); if (response.StatusCode != AmqpResponseStatusCode.OK) { throw response.ToMessagingContractException(); } }
/// <summary> /// /// </summary> /// <param name="message"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task <long> ScheduleMessageInternalAsync( ServiceBusMessage message, TimeSpan timeout, CancellationToken cancellationToken = default) { var stopWatch = Stopwatch.StartNew(); using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message)) { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.ScheduleMessageOperation, timeout, null); if (_sendLink.TryGetOpenedObject(out SendingAmqpLink sendLink)) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name; } ArraySegment <byte>[] payload = amqpMessage.GetPayload(); var buffer = new BufferListStream(payload); ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length); var entry = new AmqpMap(); { entry[ManagementConstants.Properties.Message] = value; entry[ManagementConstants.Properties.MessageId] = message.MessageId; if (!string.IsNullOrWhiteSpace(message.SessionId)) { entry[ManagementConstants.Properties.SessionId] = message.SessionId; } if (!string.IsNullOrWhiteSpace(message.PartitionKey)) { entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey; } if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey)) { entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey; } } request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> { entry }; RequestResponseAmqpLink mgmtLink = await _managementLink.GetOrCreateAsync( UseMinimum(_connectionScope.SessionTimeout, timeout.CalculateRemaining(stopWatch.Elapsed))) .ConfigureAwait(false); using AmqpMessage response = await mgmtLink.RequestAsync( request.AmqpMessage, timeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response); if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK) { var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers); if (sequenceNumbers == null || sequenceNumbers.Length < 1) { throw new ServiceBusException(true, "Could not schedule message successfully."); } return(sequenceNumbers[0]); } else { throw amqpResponseMessage.ToMessagingContractException(); } } }
/// <summary> /// Receives a batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition. /// </summary> /// /// <param name="maximumMessageCount">The maximum number of messages to receive in this batch.</param> /// <param name="maximumWaitTime">The maximum amount of time to wait to build up the requested message count for the batch; if not specified, the per-try timeout specified by the retry policy will be used.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>The batch of <see cref="ServiceBusMessage" /> from the Service Bus entity partition this consumer is associated with. If no events are present, an empty enumerable is returned.</returns> /// public override async Task <IEnumerable <ServiceBusMessage> > ReceiveAsync( int maximumMessageCount, TimeSpan?maximumWaitTime, CancellationToken cancellationToken) { Argument.AssertNotClosed(_closed, nameof(AmqpConsumer)); Argument.AssertAtLeast(maximumMessageCount, 1, nameof(maximumMessageCount)); var receivedMessageCount = 0; var failedAttemptCount = 0; var tryTimeout = _retryPolicy.CalculateTryTimeout(0); var waitTime = (maximumWaitTime ?? tryTimeout); var link = default(ReceivingAmqpLink); var retryDelay = default(TimeSpan?); var amqpMessages = default(IEnumerable <AmqpMessage>); var receivedMessages = default(List <ServiceBusMessage>); var stopWatch = Stopwatch.StartNew(); try { while (!cancellationToken.IsCancellationRequested) { try { ServiceBusEventSource.Log.MessageReceiveStart(EntityName); link = await ReceiveLink.GetOrCreateAsync(UseMinimum(ConnectionScope.SessionTimeout, tryTimeout)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); var messagesReceived = await Task.Factory.FromAsync ( (callback, state) => link.BeginReceiveRemoteMessages(maximumMessageCount, TimeSpan.FromMilliseconds(20), waitTime, callback, state), (asyncResult) => link.EndReceiveMessages(asyncResult, out amqpMessages), TaskCreationOptions.RunContinuationsAsynchronously ).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); // If event messages were received, then package them for consumption and // return them. if ((messagesReceived) && (amqpMessages != null)) { receivedMessages ??= new List <ServiceBusMessage>(); foreach (AmqpMessage message in amqpMessages) { //link.DisposeDelivery(message, true, AmqpConstants.AcceptedOutcome); receivedMessages.Add(AmqpMessageConverter.AmqpMessageToSBMessage(message)); message.Dispose(); } receivedMessageCount = receivedMessages.Count; return(receivedMessages); } // No events were available. return(Enumerable.Empty <ServiceBusMessage>()); } catch (ServiceBusException ex) when(ex.Reason == ServiceBusException.FailureReason.ServiceTimeout) { // Because the timeout specified with the request is intended to be the maximum // amount of time to wait for events, a timeout isn't considered an error condition, // rather a sign that no events were available in the requested period. return(Enumerable.Empty <ServiceBusMessage>()); } catch (Exception ex) { Exception activeEx = ex.TranslateServiceException(EntityName); // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop. // Otherwise, bubble the exception. ++failedAttemptCount; retryDelay = _retryPolicy.CalculateRetryDelay(activeEx, failedAttemptCount); if ((retryDelay.HasValue) && (!ConnectionScope.IsDisposed) && (!cancellationToken.IsCancellationRequested)) { ServiceBusEventSource.Log.MessageReceiveError(EntityName, activeEx.Message); await Task.Delay(UseMinimum(retryDelay.Value, waitTime.CalculateRemaining(stopWatch.Elapsed)), cancellationToken).ConfigureAwait(false); tryTimeout = _retryPolicy.CalculateTryTimeout(failedAttemptCount); } else if (ex is AmqpException) { throw activeEx; } else { throw; } } } // If no value has been returned nor exception thrown by this point, // then cancellation has been requested. throw new TaskCanceledException(); } catch (Exception ex) { ServiceBusEventSource.Log.MessageReceiveError(EntityName, ex.Message); throw; } finally { stopWatch.Stop(); ServiceBusEventSource.Log.MessageReceiveComplete(EntityName, receivedMessageCount); } }
/// <summary> /// /// </summary> /// <param name="message"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task <long> ScheduleMessageInternalAsync( ServiceBusMessage message, TimeSpan timeout, CancellationToken cancellationToken = default) { var sendLink = default(SendingAmqpLink); try { using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message)) { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.ScheduleMessageOperation, timeout, null); if (_sendLink.TryGetOpenedObject(out sendLink)) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name; } ArraySegment <byte>[] payload = amqpMessage.GetPayload(); var buffer = new BufferListStream(payload); ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length); var entry = new AmqpMap(); { entry[ManagementConstants.Properties.Message] = value; entry[ManagementConstants.Properties.MessageId] = message.MessageId; if (!string.IsNullOrWhiteSpace(message.SessionId)) { entry[ManagementConstants.Properties.SessionId] = message.SessionId; } if (!string.IsNullOrWhiteSpace(message.PartitionKey)) { entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey; } if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey)) { entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey; } } request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> { entry }; AmqpResponseMessage amqpResponseMessage = await ManagementUtilities.ExecuteRequestResponseAsync( _connectionScope, _managementLink, request, timeout).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK) { var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers); if (sequenceNumbers == null || sequenceNumbers.Length < 1) { throw new ServiceBusException(true, "Could not schedule message successfully."); } return(sequenceNumbers[0]); } else { throw amqpResponseMessage.ToMessagingContractException(); } } } catch (Exception exception) { ExceptionDispatchInfo.Capture(AmqpExceptionHelper.TranslateException( exception, sendLink?.GetTrackingId(), null, HasLinkCommunicationError(sendLink))) .Throw(); throw; // will never be reached } }
/// <summary> /// /// </summary> /// <param name="retryPolicy"></param> /// <param name="fromSequenceNumber"></param> /// <param name="messageCount"></param> /// <param name="sessionId"></param> /// <param name="receiveLinkName"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task <IEnumerable <ServiceBusMessage> > PeekInternal( ServiceBusRetryPolicy retryPolicy, long?fromSequenceNumber, int messageCount, string sessionId, string receiveLinkName, TimeSpan timeout, CancellationToken cancellationToken = default) { var stopWatch = new Stopwatch(); stopWatch.Start(); AmqpRequestMessage amqpRequestMessage = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.PeekMessageOperation, timeout, null); await AquireAccessTokenAsync(cancellationToken).ConfigureAwait(false); if (receiveLinkName != null) { // include associated link for service optimization amqpRequestMessage.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } amqpRequestMessage.Map[ManagementConstants.Properties.FromSequenceNumber] = fromSequenceNumber ?? LastPeekedSequenceNumber + 1; amqpRequestMessage.Map[ManagementConstants.Properties.MessageCount] = messageCount; if (!string.IsNullOrWhiteSpace(sessionId)) { amqpRequestMessage.Map[ManagementConstants.Properties.SessionId] = sessionId; } RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync( UseMinimum(ConnectionScope.SessionTimeout, timeout.CalculateRemaining(stopWatch.Elapsed))) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); using AmqpMessage responseAmqpMessage = await link.RequestAsync( amqpRequestMessage.AmqpMessage, timeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(responseAmqpMessage); var messages = new List <ServiceBusMessage>(); //AmqpError.ThrowIfErrorResponse(responseAmqpMessage, EntityName); if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK) { ServiceBusMessage message = null; IEnumerable <AmqpMap> messageList = amqpResponseMessage.GetListValue <AmqpMap>(ManagementConstants.Properties.Messages); foreach (AmqpMap entry in messageList) { var payload = (ArraySegment <byte>)entry[ManagementConstants.Properties.Message]; var amqpMessage = AmqpMessage.CreateAmqpStreamMessage(new BufferListStream(new[] { payload }), true); message = AmqpMessageConverter.AmqpMessageToSBMessage(amqpMessage, true); messages.Add(message); } if (message != null) { LastPeekedSequenceNumber = message.SystemProperties.SequenceNumber; } return(messages); } if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.NoContent || (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.NotFound && Equals(AmqpClientConstants.MessageNotFoundError, amqpResponseMessage.GetResponseErrorCondition()))) { return(messages); } // TODO throw correct exception throw new Exception(); }
/// <summary> /// /// </summary> /// <param name="message"></param> /// <param name="retryPolicy"></param> /// <param name="receiveLinkName"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public override async Task <long> ScheduleMessageAsync( ServiceBusMessage message, ServiceBusRetryPolicy retryPolicy, string receiveLinkName = null, CancellationToken cancellationToken = default) { var failedAttemptCount = 0; var stopWatch = Stopwatch.StartNew(); try { TimeSpan tryTimeout = retryPolicy.CalculateTryTimeout(0); while (!cancellationToken.IsCancellationRequested) { try { using (AmqpMessage amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message)) { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.ScheduleMessageOperation, tryTimeout, null); if (receiveLinkName != null) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } ArraySegment <byte>[] payload = amqpMessage.GetPayload(); var buffer = new BufferListStream(payload); ArraySegment <byte> value = buffer.ReadBytes((int)buffer.Length); var entry = new AmqpMap(); { entry[ManagementConstants.Properties.Message] = value; entry[ManagementConstants.Properties.MessageId] = message.MessageId; if (!string.IsNullOrWhiteSpace(message.SessionId)) { entry[ManagementConstants.Properties.SessionId] = message.SessionId; } if (!string.IsNullOrWhiteSpace(message.PartitionKey)) { entry[ManagementConstants.Properties.PartitionKey] = message.PartitionKey; } if (!string.IsNullOrWhiteSpace(message.ViaPartitionKey)) { entry[ManagementConstants.Properties.ViaPartitionKey] = message.ViaPartitionKey; } } request.Map[ManagementConstants.Properties.Messages] = new List <AmqpMap> { entry }; RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync( UseMinimum(ConnectionScope.SessionTimeout, tryTimeout.CalculateRemaining(stopWatch.Elapsed))) .ConfigureAwait(false); using AmqpMessage response = await link.RequestAsync( request.AmqpMessage, tryTimeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response); if (amqpResponseMessage.StatusCode == AmqpResponseStatusCode.OK) { var sequenceNumbers = amqpResponseMessage.GetValue <long[]>(ManagementConstants.Properties.SequenceNumbers); if (sequenceNumbers == null || sequenceNumbers.Length < 1) { throw new ServiceBusException(true, "Could not schedule message successfully."); } return(sequenceNumbers[0]); } else { throw new Exception(); //throw response.ToMessagingContractException(); } } } catch (Exception ex) { // Determine if there should be a retry for the next attempt; if so enforce the delay but do not quit the loop. // Otherwise, mark the exception as active and break out of the loop. ++failedAttemptCount; TimeSpan?retryDelay = retryPolicy.CalculateRetryDelay(ex, failedAttemptCount); if (retryDelay.HasValue && !ConnectionScope.IsDisposed && !cancellationToken.IsCancellationRequested) { ServiceBusEventSource.Log.ScheduleMessageError(EntityName, ex.Message); await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false); tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount); stopWatch.Reset(); } } } // If no value has been returned nor exception thrown by this point, // then cancellation has been requested. throw new TaskCanceledException(); } catch (Exception ex) { ServiceBusEventSource.Log.ScheduleMessageError(EntityName, ex.Message); throw; } finally { stopWatch.Stop(); ServiceBusEventSource.Log.ScheduleMessageComplete(EntityName); } }
/// <summary> /// Updates the disposition status of deferred messages. /// </summary> /// /// <param name="lockTokens">Message lock tokens to update disposition status.</param> /// <param name="timeout"></param> /// <param name="dispositionStatus"></param> /// <param name="isSessionReceiver"></param> /// <param name="sessionId"></param> /// <param name="receiveLinkName"></param> /// <param name="propertiesToModify"></param> /// <param name="deadLetterReason"></param> /// <param name="deadLetterDescription"></param> internal override async Task DisposeMessageRequestResponseAsync( Guid[] lockTokens, TimeSpan timeout, DispositionStatus dispositionStatus, bool isSessionReceiver, string sessionId = null, string receiveLinkName = null, IDictionary <string, object> propertiesToModify = null, string deadLetterReason = null, string deadLetterDescription = null) { try { // Create an AmqpRequest Message to update disposition var amqpRequestMessage = AmqpRequestMessage.CreateRequest(ManagementConstants.Operations.UpdateDispositionOperation, timeout, null); if (receiveLinkName != null) { amqpRequestMessage.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } amqpRequestMessage.Map[ManagementConstants.Properties.LockTokens] = lockTokens; amqpRequestMessage.Map[ManagementConstants.Properties.DispositionStatus] = dispositionStatus.ToString().ToLowerInvariant(); if (deadLetterReason != null) { amqpRequestMessage.Map[ManagementConstants.Properties.DeadLetterReason] = deadLetterReason; } if (deadLetterDescription != null) { amqpRequestMessage.Map[ManagementConstants.Properties.DeadLetterDescription] = deadLetterDescription; } if (propertiesToModify != null) { var amqpPropertiesToModify = new AmqpMap(); foreach (var pair in propertiesToModify) { if (AmqpMessageConverter.TryGetAmqpObjectFromNetObject(pair.Value, MappingType.ApplicationProperty, out var amqpObject)) { amqpPropertiesToModify[new MapKey(pair.Key)] = amqpObject; } else { throw new NotSupportedException( Resources.InvalidAmqpMessageProperty.FormatForUser(pair.Key.GetType())); } } if (amqpPropertiesToModify.Count > 0) { amqpRequestMessage.Map[ManagementConstants.Properties.PropertiesToModify] = amqpPropertiesToModify; } } if (!string.IsNullOrWhiteSpace(sessionId)) { amqpRequestMessage.Map[ManagementConstants.Properties.SessionId] = sessionId; } if (isSessionReceiver) { // TODO - ThrowIfSessionLockLost(); } var amqpResponseMessage = await ExecuteRequestResponseAsync(amqpRequestMessage, timeout).ConfigureAwait(false); if (amqpResponseMessage.StatusCode != AmqpResponseStatusCode.OK) { // throw amqpResponseMessage.ToMessagingContractException(); } } catch (Exception) { // throw AmqpExceptionHelper.GetClientException(exception); throw; } }
public static AmqpMessage BatchSBMessagesAsAmqpMessage(IEnumerable <SBMessage> sbMessages) { if (sbMessages == null) { throw Fx.Exception.ArgumentNull(nameof(sbMessages)); } AmqpMessage amqpMessage; AmqpMessage firstAmqpMessage = null; SBMessage firstMessage = null; List <Data> dataList = null; var messageCount = 0; foreach (var sbMessage in sbMessages) { messageCount++; amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(sbMessage); if (firstAmqpMessage == null) { firstAmqpMessage = amqpMessage; firstMessage = sbMessage; continue; } if (dataList == null) { dataList = new List <Data> { ToData(firstAmqpMessage) }; } dataList.Add(ToData(amqpMessage)); } if (messageCount == 1 && firstAmqpMessage != null) { firstAmqpMessage.Batchable = true; return(firstAmqpMessage); } amqpMessage = AmqpMessage.Create(dataList); amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat; if (firstMessage.MessageId != null) { amqpMessage.Properties.MessageId = firstMessage.MessageId; } if (firstMessage.SessionId != null) { amqpMessage.Properties.GroupId = firstMessage.SessionId; } if (firstMessage.PartitionKey != null) { amqpMessage.MessageAnnotations.Map[AmqpMessageConverter.PartitionKeyName] = firstMessage.PartitionKey; } if (firstMessage.ViaPartitionKey != null) { amqpMessage.MessageAnnotations.Map[AmqpMessageConverter.ViaPartitionKeyName] = firstMessage.ViaPartitionKey; } amqpMessage.Batchable = true; return(amqpMessage); }