internal static async Task <AmqpResponseMessage> ExecuteRequestResponseAsync( AmqpConnectionScope connectionScope, FaultTolerantAmqpObject <RequestResponseAmqpLink> managementLink, AmqpRequestMessage amqpRequestMessage, string transactionGroup, TimeSpan timeout) { AmqpMessage amqpMessage = amqpRequestMessage.AmqpMessage; ArraySegment <byte> transactionId = AmqpConstants.NullBinary; var ambientTransaction = Transaction.Current; if (ambientTransaction != null) { transactionId = await AmqpTransactionManager.Instance.EnlistAsync( ambientTransaction, connectionScope, transactionGroup, timeout) .ConfigureAwait(false); } if (!managementLink.TryGetOpenedObject(out var requestResponseAmqpLink)) { // MessagingEventSource.Log.CreatingNewLink(this.ClientId, this.isSessionReceiver, this.SessionIdInternal, true, this.LinkException); requestResponseAmqpLink = await managementLink.GetOrCreateAsync(timeout).ConfigureAwait(false); } var responseAmqpMessage = await Task.Factory.FromAsync( (c, s) => requestResponseAmqpLink.BeginRequest(amqpMessage, transactionId, timeout, c, s), (a) => requestResponseAmqpLink.EndRequest(a), null).ConfigureAwait(false); return(AmqpResponseMessage.CreateResponse(responseAmqpMessage)); }
/// <summary> /// /// </summary> /// <param name="amqpRequestMessage"></param> /// <param name="timeout"></param> /// <returns></returns> internal async Task <AmqpResponseMessage> ExecuteRequestResponseAsync( AmqpRequestMessage amqpRequestMessage, TimeSpan timeout) { var amqpMessage = amqpRequestMessage.AmqpMessage; ArraySegment <byte> transactionId = AmqpConstants.NullBinary; //var ambientTransaction = Transaction.Current; //if (ambientTransaction != null) //{ // transactionId = await AmqpTransactionManager.Instance.EnlistAsync(ambientTransaction, this.ServiceBusConnection).ConfigureAwait(false); //} if (!ManagementLink.TryGetOpenedObject(out var requestResponseAmqpLink)) { // MessagingEventSource.Log.CreatingNewLink(this.ClientId, this.isSessionReceiver, this.SessionIdInternal, true, this.LinkException); requestResponseAmqpLink = await ManagementLink.GetOrCreateAsync(timeout).ConfigureAwait(false); } var responseAmqpMessage = await Task.Factory.FromAsync( (c, s) => requestResponseAmqpLink.BeginRequest(amqpMessage, transactionId, timeout, c, s), (a) => requestResponseAmqpLink.EndRequest(a), this).ConfigureAwait(false); return(AmqpResponseMessage.CreateResponse(responseAmqpMessage)); }
/// <summary> /// /// </summary> /// <param name="sequenceNumber"></param> /// <param name="retryPolicy"></param> /// <param name="receiveLinkName"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task CancelScheduledMessageInternal( long sequenceNumber, ServiceBusRetryPolicy retryPolicy, string receiveLinkName, TimeSpan timeout, CancellationToken cancellationToken = default) { var stopWatch = Stopwatch.StartNew(); var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.CancelScheduledMessageOperation, timeout, null); if (receiveLinkName != null) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } request.Map[ManagementConstants.Properties.SequenceNumbers] = new[] { sequenceNumber }; RequestResponseAmqpLink link = await ManagementLink.GetOrCreateAsync( UseMinimum(ConnectionScope.SessionTimeout, timeout.CalculateRemaining(stopWatch.Elapsed))) .ConfigureAwait(false); using AmqpMessage response = await link.RequestAsync( request.AmqpMessage, timeout.CalculateRemaining(stopWatch.Elapsed)) .ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); AmqpResponseMessage amqpResponseMessage = AmqpResponseMessage.CreateResponse(response); if (amqpResponseMessage.StatusCode != AmqpResponseStatusCode.OK) { throw new Exception(); //throw response.ToMessagingContractException(); } return; }
/// <summary> /// /// </summary> /// <param name="sequenceNumber"></param> /// <param name="timeout"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal async Task CancelScheduledMessageInternalAsync( long sequenceNumber, TimeSpan timeout, CancellationToken cancellationToken = default) { var stopWatch = Stopwatch.StartNew(); var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.CancelScheduledMessageOperation, timeout, null); if (_sendLink.TryGetOpenedObject(out SendingAmqpLink sendLink)) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = sendLink.Name; } request.Map[ManagementConstants.Properties.SequenceNumbers] = new[] { sequenceNumber }; 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) { throw amqpResponseMessage.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> /// /// </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> /// /// </summary> /// <param name="sequenceNumber"></param> /// <param name="retryPolicy"></param> /// <param name="receiveLinkName"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public override async Task CancelScheduledMessageAsync( long sequenceNumber, ServiceBusRetryPolicy retryPolicy, string receiveLinkName = null, CancellationToken cancellationToken = default) { var failedAttemptCount = 0; var stopWatch = Stopwatch.StartNew(); try { var tryTimeout = retryPolicy.CalculateTryTimeout(0); while (!cancellationToken.IsCancellationRequested) { try { var request = AmqpRequestMessage.CreateRequest( ManagementConstants.Operations.CancelScheduledMessageOperation, tryTimeout, null); if (receiveLinkName != null) { request.AmqpMessage.ApplicationProperties.Map[ManagementConstants.Request.AssociatedLinkName] = receiveLinkName; } request.Map[ManagementConstants.Properties.SequenceNumbers] = new[] { sequenceNumber }; 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) { throw new Exception(); //throw response.ToMessagingContractException(); } return; } 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.CancelScheduledMessageError(EntityName, ex.Message); await Task.Delay(retryDelay.Value, cancellationToken).ConfigureAwait(false); tryTimeout = retryPolicy.CalculateTryTimeout(failedAttemptCount); stopWatch.Reset(); } else { throw ex; } } } // 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.CancelScheduledMessageError(EntityName, ex.Message); throw; } finally { stopWatch.Stop(); ServiceBusEventSource.Log.CancelScheduledMessageComplete(EntityName); } }