/// <summary> /// Receives the next message (if any) from the transport's input queue <see cref="ITransport.Address"/> /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (_inputQueueName == null) { throw new InvalidOperationException("This Azure Storage Queues transport does not have an input queue, hence it is not possible to receive anything"); } var inputQueue = GetQueue(_inputQueueName); var cloudQueueMessage = await inputQueue.GetMessageAsync(_initialVisibilityDelay, new QueueRequestOptions(), new OperationContext(), cancellationToken); if (cloudQueueMessage == null) { return(null); } context.OnCompleted(async() => { // if we get this far, don't pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await inputQueue.DeleteMessageAsync(cloudQueueMessage); }); context.OnAborted(() => { const MessageUpdateFields fields = MessageUpdateFields.Visibility; var visibilityTimeout = TimeSpan.FromSeconds(0); AsyncHelpers.RunSync(() => inputQueue.UpdateMessageAsync(cloudQueueMessage, visibilityTimeout, fields)); }); return(Deserialize(cloudQueueMessage)); }
/// <summary> /// Receives the next message (if any) from the transport's input queue <see cref="ITransport.Address"/> /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context) { if (_inputQueueName == null) { throw new InvalidOperationException("This Azure Storage Queues transport does not have an input queue, hence it is not possible to reveive anything"); } var inputQueue = GetQueue(_inputQueueName); var cloudQueueMessage = await inputQueue.GetMessageAsync(_initialVisibilityDelay, new QueueRequestOptions(), new OperationContext()); if (cloudQueueMessage == null) { return(null); } context.OnCompleted(async() => { await inputQueue.DeleteMessageAsync(cloudQueueMessage); }); context.OnAborted(() => { inputQueue.UpdateMessage(cloudQueueMessage, TimeSpan.FromSeconds(0), MessageUpdateFields.Visibility); }); return(Deserialize(cloudQueueMessage)); }
/// <summary> /// Receives the next message from the queue identified by the configured <see cref="AbstractRebusTransport.Address"/>, returning null if none was available /// </summary> public override async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (_inputQueueAddress == null) { throw new InvalidOperationException("This in-mem transport is initialized without an input queue, hence it is not possible to receive anything!"); } var nextMessage = _network.GetNextOrNull(_inputQueueAddress); if (nextMessage == null) { return(null); } context.OnAborted(() => { _network.Deliver(_inputQueueAddress, nextMessage, alwaysQuiet: true); }); return(nextMessage.ToTransportMessage()); }
/// <summary> /// Responsible for releasing the lease on message failure and removing the message on transaction commit /// </summary> /// <param name="context">Transaction context of the message processing</param> /// <param name="messageId">Identifier of the message currently being processed</param> private void ApplyTransactionSemantics(ITransactionContext context, long messageId) { context.OnAborted( async ctx => { try { await ClearProcessing(messageId).ConfigureAwait(false); } catch (Exception ex) { _log.Error(ex, "While Resetting Lease"); } } ); context.OnCommitted( async ctx => { try { await DeleteMessage(messageId).ConfigureAwait(false); } catch (Exception ex) { _log.Error(ex, "While Deleting Message"); } } ); }
/// <summary> /// Receives the next transport message or null if none is available. Can block if it wants to, just respect the <paramref name="cancellationToken"/> /// </summary> public override async Task<TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (amqpReceiver == null) return null; AmqpLite.Message msg = await amqpReceiver.ReceiveAsync(TimeSpan.FromSeconds(0.5)); if (msg == null) return null; if (!(msg.Properties?.AbsoluteExpiryTime != DateTime.MinValue && msg.Properties?.AbsoluteExpiryTime.ToUniversalTime() > DateTime.Now.ToUniversalTime())) return null; context.OnCompleted(async ctx => { amqpReceiver.Accept(msg); }); context.OnAborted(async ctx => { amqpReceiver.Modify(msg, true); }); context.OnDisposed(async ctx => { //amqpReceiver.Close(); }); var result = new TransportMessage(GetHeaders(msg), GetBytes(msg.Body.ToString())); return result; }
public async Task <TransportMessage> Receive(ITransactionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (Address == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to reveive anything"); } var client = GetClientFromTransactionContext(context); var request = new ReceiveMessageRequest(_queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = 1, AttributeNames = new List <string>(new[] { "All" }), MessageAttributeNames = new List <string>(new[] { "All" }) }; var response = await client.ReceiveMessageAsync(request); if (!response.Messages.Any()) { return(null); } var message = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(message, client); context.OnCompleted(async() => { renewalTask.Dispose(); await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); }); context.OnAborted(() => { renewalTask.Dispose(); client.ChangeMessageVisibility(_queueUrl, message.ReceiptHandle, 0); }); if (MessageIsExpired(message)) { await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); return(null); } renewalTask.Start(); var transportMessage = GetTransportMessage(message); return(transportMessage); }
public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { var networkToUse = context.GetOrAdd(CurrentNetworkConnectionKey, () => _network); var nextMessage = networkToUse.GetNextOrNull(Address); if (nextMessage != null) { context.OnAborted(_ => networkToUse.Deliver(Address, nextMessage, alwaysQuiet: true)); return(nextMessage.ToTransportMessage()); } return(null); }
public Task Send(string destinationAddress, TransportMessage message, ITransactionContext context) { if (destinationAddress == null) { throw new ArgumentNullException("destinationAddress"); } if (message == null) { throw new ArgumentNullException("message"); } if (context == null) { throw new ArgumentNullException("context"); } var outputQueue = context.GetOrAdd(OutgoingQueueContextKey, () => new InMemOutputQueue()); var contextActionsSet = context.GetOrAdd(OutgoingQueueContextActionIsSetKey, () => false); if (!contextActionsSet) { context.OnCommitted(async() => { var client = GetClientFromTransactionContext(context); var messageSendRequests = outputQueue.GetMessages(); var tasks = messageSendRequests.Select(r => client.SendMessageBatchAsync(new SendMessageBatchRequest(r.DestinationAddressUrl, r.Messages.ToList()))); var response = await Task.WhenAll(tasks); if (response.Any(r => r.Failed.Any())) { GenerateErrorsAndThrow(response); } }); context.OnAborted(outputQueue.Clear); context.Items[OutgoingQueueContextActionIsSetKey] = true; } var sendMessageRequest = new SendMessageBatchRequestEntry() { MessageAttributes = CreateAttributesFromHeaders(message.Headers), MessageBody = GetBody(message.Body), Id = message.Headers.GetValueOrNull(Headers.MessageId) ?? Guid.NewGuid().ToString(), }; outputQueue.AddMessage(GetDestinationQueueUrlByName(destinationAddress, context), sendMessageRequest); return(_emptyTask); }
/// <summary> /// Receives the next message from the queue identified by the configured <see cref="Address"/>, returning null if none was available /// </summary> public async Task<TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (context == null) throw new ArgumentNullException(nameof(context)); if (_inputQueueAddress == null) throw new InvalidOperationException("This in-mem transport is initialized without an input queue, hence it is not possible to receive anything!"); var nextMessage = _network.GetNextOrNull(_inputQueueAddress); if (nextMessage == null) return null; context.OnAborted(() => { _network.Deliver(_inputQueueAddress, nextMessage, alwaysQuiet: true); }); return nextMessage.ToTransportMessage(); }
public async Task <TransportMessage> Receive(ITransactionContext context) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue, hence it is not possible to reveive anything"); } var model = GetModel(context); var result = model.BasicGet(Address, false); if (result == null) { return(null); } var deliveryTag = result.DeliveryTag; context.OnCompleted(async() => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { model.BasicNack(deliveryTag, false, true); }); var headers = result.BasicProperties.Headers .ToDictionary(kvp => kvp.Key, kvp => { var headerValue = kvp.Value; if (headerValue is byte[]) { var stringHeaderValue = HeaderValueEncoding.GetString((byte[])headerValue); return(stringHeaderValue); } return(headerValue.ToString()); }); return(new TransportMessage(headers, result.Body)); }
/// <summary> /// Responsible for releasing the lease on message failure and removing the message on transaction commit /// </summary> /// <param name="context">Transaction context of the message processing</param> /// <param name="messageId">Identifier of the message currently being processed</param> private void ApplyTransactionSemantics(ITransactionContext context, long messageId) { AutomaticLeaseRenewer renewal = null; if (_automaticLeaseRenewal == true) { renewal = new AutomaticLeaseRenewer(ReceiveTableName.QualifiedName, messageId, ConnectionProvider, _automaticLeaseRenewalIntervalMilliseconds, _leaseIntervalMilliseconds); } context.OnAborted( () => { renewal?.Dispose(); AsyncHelpers.RunSync(() => UpdateLease(ConnectionProvider, ReceiveTableName.QualifiedName, messageId, null)); } ); context.OnCommitted( async() => { renewal?.Dispose(); // Delete the message using (var deleteConnection = await ConnectionProvider.GetConnection()) { using (var deleteCommand = deleteConnection.CreateCommand()) { deleteCommand.CommandType = CommandType.Text; deleteCommand.CommandText = $@" DELETE FROM {ReceiveTableName.QualifiedName} WITH (ROWLOCK) WHERE id = @id "; deleteCommand.Parameters.Add("@id", SqlDbType.BigInt).Value = messageId; deleteCommand.ExecuteNonQuery(); } await deleteConnection.Complete(); } } ); }
/// <summary> /// Receives the next message from the queue identified by the configured <see cref="Address"/>, returning null if none was available /// </summary> public async Task<TransportMessage> Receive(ITransactionContext context) { if (context == null) throw new ArgumentNullException("context"); var nextMessage = _network.GetNextOrNull(_inputQueueAddress); if (nextMessage != null) { context.OnAborted(() => { _network.Deliver(_inputQueueAddress, nextMessage, alwaysQuiet: true); }); return nextMessage.ToTransportMessage(); } await Task.Delay(20); return null; }
/// <summary> /// Sends /// </summary> /// <param name="destinationAddress"></param> /// <param name="message"></param> /// <param name="context"></param> /// <returns></returns> public async Task Send(string destinationAddress, TransportMessage message, ITransactionContext context) { // this timestamp will only be used in the file names of message files written to approach some kind // of global ordering - individual messages sent from this context will have sequence numbers on them // in addition to the timestamp var time = _rebusTime.Now; var outgoingMessages = context.GetOrAdd("file-system-transport-outgoing-messages", () => { var queue = new ConcurrentQueue <OutgoingMessage>(); context.OnCommitted(_ => SendOutgoingMessages(queue, time)); context.OnAborted(_ => AbortOutgoingMessages(queue)); return(queue); }); var outgoingMessage = await OutgoingMessage.WriteTemp(GetDirectory(destinationAddress), message); outgoingMessages.Enqueue(outgoingMessage); }
static void SetUpCompletion(ITransactionContext context, CloudQueueMessage cloudQueueMessage, CloudQueue inputQueue) { var messageId = cloudQueueMessage.Id; var popReceipt = cloudQueueMessage.PopReceipt; context.OnCompleted(async() => { try { // if we get this far, don't pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await inputQueue.DeleteMessageAsync( messageId, popReceipt, ExponentialRetryRequestOptions, new OperationContext() ); } catch (Exception exception) { throw new RebusApplicationException(exception, $"Could not delete message with ID {messageId} and pop receipt {popReceipt} from the input queue"); } }); context.OnAborted(() => { const MessageUpdateFields fields = MessageUpdateFields.Visibility; var visibilityTimeout = TimeSpan.FromSeconds(0); AsyncHelpers.RunSync(async() => { // ignore if this fails try { await inputQueue.UpdateMessageAsync(cloudQueueMessage, visibilityTimeout, fields); } catch { } }); }); }
/// <summary> /// Responsible for releasing the lease on message failure and removing the message on transaction commit /// </summary> /// <param name="context">Transaction context of the message processing</param> /// <param name="messageId">Identifier of the message currently being processed</param> /// <param name="cancellationToken">Token to abort processing</param> private void ApplyTransactionSemantics(ITransactionContext context, long messageId, CancellationToken cancellationToken) { AutomaticLeaseRenewer renewal = null; if (_automaticLeaseRenewal == true) { renewal = new AutomaticLeaseRenewer( this, ReceiveTableName.QualifiedName, messageId, ConnectionProvider, _automaticLeaseRenewalInterval, _leaseInterval, cancellationToken); } context.OnAborted( ctx => { renewal?.Dispose(); try { AsyncHelpers.RunSync(() => UpdateLease(ConnectionProvider, ReceiveTableName.QualifiedName, messageId, null, cancellationToken)); } catch (Exception ex) { Log.Error(ex, "While Resetting Lease"); } } ); context.OnCommitted( async ctx => { renewal?.Dispose(); try { await DeleteMessage(messageId, cancellationToken); } catch (Exception ex) { Log.Error(ex, "While Deleteing Message"); } } ); }
public async Task <TransportMessage> Receive(ITransactionContext context) { var model = GetModel(context); var result = model.BasicGet(Address, false); if (result == null) { return(null); } var deliveryTag = result.DeliveryTag; context.OnCompleted(async() => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { model.BasicNack(deliveryTag, false, true); }); var headers = result.BasicProperties.Headers .ToDictionary(kvp => kvp.Key, kvp => { var headerValue = kvp.Value; if (headerValue is byte[]) { var stringHeaderValue = HeaderValueEncoding.GetString((byte[])headerValue); return(stringHeaderValue); } return(headerValue.ToString()); }); return(new TransportMessage(headers, result.Body)); }
/// <summary> /// Receives the next message from the queue identified by the configured <see cref="Address"/>, returning null if none was available /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context) { if (context == null) { throw new ArgumentNullException("context"); } var nextMessage = _network.GetNextOrNull(_inputQueueAddress); if (nextMessage != null) { context.OnAborted(() => { _network.Deliver(_inputQueueAddress, nextMessage, alwaysQuiet: true); }); return(nextMessage.ToTransportMessage()); } await Task.Delay(20); return(null); }
/// <summary> /// Receives the next message (if any) from the transport's input queue <see cref="ITransport.Address"/> /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context) { var inputQueue = GetQueue(_inputQueueName); var cloudQueueMessage = await inputQueue.GetMessageAsync(_initialVisibilityDelay, new QueueRequestOptions(), new OperationContext()); if (cloudQueueMessage == null) { return(null); } context.OnCompleted(async() => { await inputQueue.DeleteMessageAsync(cloudQueueMessage); }); context.OnAborted(() => { inputQueue.UpdateMessage(cloudQueueMessage, TimeSpan.FromSeconds(0), MessageUpdateFields.Visibility); }); return(Deserialize(cloudQueueMessage)); }
public override async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (_subscriberClient == null) { return(null); } ReceivedMessage receivedMessage = null; try { var response = await _subscriberClient.PullAsync( new PullRequest() { SubscriptionAsSubscriptionName = _subscriptionName, MaxMessages = 1 }, CallSettings.FromCancellationToken(cancellationToken) ); receivedMessage = response.ReceivedMessages.FirstOrDefault(); } catch (RpcException ex) when(ex.Status.StatusCode == StatusCode.Unavailable) { throw new RebusApplicationException(ex, "GooglePubSub UNAVAILABLE due to too many concurrent pull requests pending for the given subscription"); } catch (RpcException ex) when(ex.StatusCode == StatusCode.Cancelled) { if (!cancellationToken.IsCancellationRequested) { //Rebus has not ordered this cancellation - therefore throwing throw new RebusApplicationException(ex, "Cancelled when fetching messages from GooglePubSub"); } } catch (Exception ex) { throw new RebusApplicationException(ex, "Failed when fetching messages from GooglePubSub"); } if (receivedMessage == null) { return(null); } var receivedTransportMessage = receivedMessage.ToRebusTransportMessage(); var utcNow = DateTimeOffset.UtcNow; if (receivedTransportMessage.IsExpired(utcNow)) { Log.Debug($"Discarded message {string.Join(",", receivedTransportMessage.Headers.Select(a => a.Key + " : " + a.Value).ToArray())} because message expired {receivedTransportMessage.AbsoluteExpiryTimeUtc()} which is lesser than current time {utcNow}"); return(null); } context.OnCompleted(async ctx => { await _subscriberClient.AcknowledgeAsync(_subscriptionName, new[] { receivedMessage.AckId }); }); context.OnAborted(async ctx => { await _subscriberClient.ModifyAckDeadlineAsync(_subscriptionName, new[] { receivedMessage.AckId }, 0); }); return(receivedTransportMessage); }
public async Task <TransportMessage> Receive(ITransactionContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (_inputQueueAddress == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to reveive anything"); } var client = GetClientFromTransactionContext(context); var response = await client.ReceiveMessageAsync(new ReceiveMessageRequest(_queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = 1, AttributeNames = new List <string>(new[] { "All" }), MessageAttributeNames = new List <string>(new[] { "All" }) }); if (response.Messages.Any()) { var message = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(message, client); context.OnCompleted(async() => { renewalTask.Dispose(); var result = await client.DeleteMessageBatchAsync(new DeleteMessageBatchRequest(_queueUrl, response.Messages .Select(m => new DeleteMessageBatchRequestEntry(m.MessageId, m.ReceiptHandle)) .ToList())); if (result.Failed.Any()) { GenerateErrorsAndLog(result); } }); context.OnAborted(() => { renewalTask.Dispose(); var result = client.ChangeMessageVisibilityBatch(new ChangeMessageVisibilityBatchRequest(_queueUrl, response.Messages .Select(m => new ChangeMessageVisibilityBatchRequestEntry(m.MessageId, m.ReceiptHandle) { VisibilityTimeout = 0 }).ToList())); if (result.Failed.Any()) { GenerateErrorsAndLog(result); } }); if (MessageIsExpired(message)) { await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); return(null); } renewalTask.Start(); var transportMessage = GetTransportMessage(message); return(transportMessage); } return(null); }
/// <summary> /// Receives the next message (if any) from the transport's input queue <see cref="ITransport.Address"/> /// </summary> public async Task<TransportMessage> Receive(ITransactionContext context) { var inputQueue = GetQueue(_inputQueueName); var cloudQueueMessage = await inputQueue.GetMessageAsync(_initialVisibilityDelay, new QueueRequestOptions(), new OperationContext()); if (cloudQueueMessage == null) return null; context.OnCompleted(async () => { await inputQueue.DeleteMessageAsync(cloudQueueMessage); }); context.OnAborted(() => { inputQueue.UpdateMessage(cloudQueueMessage, TimeSpan.FromSeconds(0), MessageUpdateFields.Visibility); }); return Deserialize(cloudQueueMessage); }
public async Task<TransportMessage> Receive(ITransactionContext context) { if (context == null) throw new ArgumentNullException("context"); if (_inputQueueAddress == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to reveive anything"); } var client = GetClientFromTransactionContext(context); var response = await client.ReceiveMessageAsync(new ReceiveMessageRequest(_queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = 1, AttributeNames = new List<string>(new[] { "All" }), MessageAttributeNames = new List<string>(new[] { "All" }) }); if (response.Messages.Any()) { var message = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(message, client); context.OnCompleted(async () => { renewalTask.Dispose(); var result = await client.DeleteMessageBatchAsync(new DeleteMessageBatchRequest(_queueUrl, response.Messages .Select(m => new DeleteMessageBatchRequestEntry(m.MessageId, m.ReceiptHandle)) .ToList())); if (result.Failed.Any()) { GenerateErrorsAndLog(result); } }); context.OnAborted(() => { renewalTask.Dispose(); var result = client.ChangeMessageVisibilityBatch(new ChangeMessageVisibilityBatchRequest(_queueUrl, response.Messages .Select(m => new ChangeMessageVisibilityBatchRequestEntry(m.MessageId, m.ReceiptHandle) { VisibilityTimeout = 0 }).ToList())); if (result.Failed.Any()) { GenerateErrorsAndLog(result); } }); if (MessageIsExpired(message)) { await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); return null; } renewalTask.Start(); var transportMessage = GetTransportMessage(message); return transportMessage; } return null; }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue - therefore, it is not possible to receive anything"); } try { if (!_consumers.TryDequeue(out var consumer)) { consumer = InitializeConsumer(); } if (consumer == null) { // initialization must have failed return(null); } if (!consumer.Model.IsOpen) { consumer.Dispose(); return(null); } context.OnDisposed(() => _consumers.Enqueue(consumer)); if (!consumer.Queue.Dequeue(TwoSeconds, out var result)) { return(null); } if (result == null) { return(null); } // ensure we use the consumer's model throughtout the handling of this message context.Items[CurrentModelItemsKey] = consumer.Model; var deliveryTag = result.DeliveryTag; context.OnCompleted(async() => { var model = GetModel(context); model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { // we might not be able to do this, but it doesn't matter that much if it succeeds try { var model = GetModel(context); model.BasicNack(deliveryTag, false, true); } catch { } }); return(CreateTransportMessage(result.BasicProperties, result.Body)); } catch (EndOfStreamException) { return(null); } catch (Exception exception) { Thread.Sleep(1000); throw new RebusApplicationException(exception, $"Unexpected exception thrown while trying to dequeue a message from rabbitmq, queue address: {Address}"); } }
public async Task <TransportMessage> Receive(ITransactionContext context) { if (_inputQueueAddress == null) { throw new InvalidOperationException("This Azure Service Bus transport does not have an input queue, hence it is not possible to reveive anything"); } using (await _bottleneck.Enter()) { var brokeredMessage = await ReceiveBrokeredMessage(); if (brokeredMessage == null) { return(null); } var headers = brokeredMessage.Properties .Where(kvp => kvp.Value is string) .ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value); var messageId = headers.GetValueOrNull(Headers.MessageId); var leaseDuration = (brokeredMessage.LockedUntilUtc - DateTime.UtcNow); var lockRenewalInterval = TimeSpan.FromMinutes(0.8 * leaseDuration.TotalMinutes); var renewalTask = GetRenewalTaskOrFakeDisposable(messageId, brokeredMessage, lockRenewalInterval); context.OnAborted(() => { renewalTask.Dispose(); try { brokeredMessage.Abandon(); } catch (Exception exception) { // if it fails, it'll be back on the queue anyway.... _log.Warn("Could not abandon message: {0}", exception); } }); context.OnCommitted(async() => renewalTask.Dispose()); context.OnCompleted(async() => { await brokeredMessage.CompleteAsync(); }); context.OnDisposed(() => { renewalTask.Dispose(); brokeredMessage.Dispose(); }); using (var memoryStream = new MemoryStream()) { await brokeredMessage.GetBody <Stream>().CopyToAsync(memoryStream); return(new TransportMessage(headers, memoryStream.ToArray())); } } }
public async Task<TransportMessage> Receive(ITransactionContext context) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue, hence it is not possible to reveive anything"); } var model = GetModel(context); var result = model.BasicGet(Address, false); if (result == null) return null; var deliveryTag = result.DeliveryTag; context.OnCompleted(async () => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { model.BasicNack(deliveryTag, false, true); }); var headers = result.BasicProperties.Headers .ToDictionary(kvp => kvp.Key, kvp => { var headerValue = kvp.Value; if (headerValue is byte[]) { var stringHeaderValue = HeaderValueEncoding.GetString((byte[])headerValue); return stringHeaderValue; } return headerValue.ToString(); }); return new TransportMessage(headers, result.Body); }
public async Task <TransportMessage> Receive(ITransactionContext context) { using (await _bottleneck.Enter()) { var brokeredMessage = await ReceiveBrokeredMessage(); if (brokeredMessage == null) { return(null); } var headers = brokeredMessage.Properties .Where(kvp => kvp.Value is string) .ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value); var messageId = headers.GetValueOrNull(Headers.MessageId); _log.Debug("Received brokered message with ID {0}", messageId); var leaseDuration = (brokeredMessage.LockedUntilUtc - DateTime.UtcNow); var lockRenewalInterval = TimeSpan.FromMinutes(0.8 * leaseDuration.TotalMinutes); var renewalTask = GetRenewalTaskOrFakeDisposable(messageId, brokeredMessage, lockRenewalInterval); context.OnAborted(() => { renewalTask.Dispose(); _log.Debug("Abandoning message with ID {0}", messageId); try { brokeredMessage.Abandon(); } catch (Exception exception) { // if it fails, it'll be back on the queue anyway.... _log.Warn("Could not abandon message: {0}", exception); } }); context.OnCommitted(async() => { renewalTask.Dispose(); }); context.OnCompleted(async() => { _log.Debug("Completing message with ID {0}", messageId); await GetRetrier().Execute(() => brokeredMessage.CompleteAsync()); }); context.OnDisposed(() => { renewalTask.Dispose(); _log.Debug("Disposing message with ID {0}", messageId); brokeredMessage.Dispose(); }); return(new TransportMessage(headers, brokeredMessage.GetBody <byte[]>())); } }
/// <summary> /// Receives the next message from the input queue. Returns null if no message was available /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { var receivedMessage = await ReceiveInternal().ConfigureAwait(false); if (receivedMessage == null) { return(null); } var message = receivedMessage.Message; var messageReceiver = receivedMessage.MessageReceiver; if (!message.SystemProperties.IsLockTokenSet) { throw new RebusApplicationException($"OMG that's weird - message with ID {message.MessageId} does not have a lock token!"); } var lockToken = message.SystemProperties.LockToken; var messageId = message.MessageId; CancellationTokenSource cancellationTokenSource = null; IAsyncTask renewalTask = null; if (AutomaticallyRenewPeekLock && !_prefetchingEnabled) { var now = DateTime.UtcNow; var leaseDuration = message.SystemProperties.LockedUntilUtc - now; var lockRenewalInterval = TimeSpan.FromMinutes(0.5 * leaseDuration.TotalMinutes); cancellationTokenSource = new CancellationTokenSource(); renewalTask = _asyncTaskFactory .Create(description: $"RenewPeekLock-{messageId}", action: () => RenewPeekLock(messageReceiver, messageId, lockToken, cancellationTokenSource), intervalSeconds: (int)lockRenewalInterval.TotalSeconds, prettyInsignificant: true ); cancellationTokenSource.Token.Register(renewalTask.Dispose); renewalTask.Start(); } context.OnCompleted(async ctx => { try { await messageReceiver.CompleteAsync(lockToken).ConfigureAwait(false); } catch (Exception exception) { throw new RebusApplicationException(exception, $"Could not complete message with ID {message.MessageId} and lock token {lockToken}"); } // Dispose the renewal task after the message has been removed. // Note that we could get a MessageLockLostException and log an error in RenewPeekLock in the off chance that renewal runs between CompleteAsync and Dispose here, // but that's better than disposing the renewal first and potentially loosing the lock before calling complete. renewalTask?.Dispose(); }); context.OnAborted(async ctx => { // Dispose the renewal before abandoning the message, otherwise renewal could grab the lock again. renewalTask?.Dispose(); try { await messageReceiver.AbandonAsync(lockToken).ConfigureAwait(false); } catch (Exception exception) { throw new RebusApplicationException(exception, $"Could not abandon message with ID {message.MessageId} and lock token {lockToken}"); } }); context.OnDisposed(ctx => { renewalTask?.Dispose(); cancellationTokenSource?.Dispose(); }); var userProperties = message.UserProperties; var headers = userProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString()); var body = message.Body; return(new TransportMessage(headers, body)); }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue - therefore, it is not possible to receive anything"); } try { if (!_consumers.TryDequeue(out var consumer)) { consumer = InitializeConsumer(); } else { try { // When a consumer is dequeued from the the "consumers" pool, it might be bound to a queue, which does not exist anymore, // eg. expired and deleted by RabittMQ server policy). In this case this calling QueueDeclarePassive will result in // an OperationInterruptedException and "consumer.Model.IsOpen" will be set to false (this is handled later in the code by // disposing this consumer). There is no need to handle this exception. The logic of InitializeConsumer() will make sure // that the queue is recreated later based on assumption about how ReBus is handling null-result of ITransport.Receive(). consumer?.Model.QueueDeclarePassive(Address); } catch { } } if (consumer == null) { // initialization must have failed return(null); } if (!consumer.Model.IsOpen) { consumer.Dispose(); return(null); } context.OnDisposed((tc) => _consumers.Enqueue(consumer)); if (!consumer.Queue.Dequeue(TwoSeconds, out var result)) { return(null); } if (result == null) { return(null); } // ensure we use the consumer's model throughtout the handling of this message context.Items[CurrentModelItemsKey] = consumer.Model; var deliveryTag = result.DeliveryTag; context.OnCompleted(async(tc) => { var model = GetModel(context); model.BasicAck(deliveryTag, false); }); context.OnAborted((tc) => { // we might not be able to do this, but it doesn't matter that much if it succeeds try { var model = GetModel(context); model.BasicNack(deliveryTag, false, true); } catch { } }); return(CreateTransportMessage(result.BasicProperties, result.Body)); } catch (EndOfStreamException) { return(null); } catch (Exception exception) { Thread.Sleep(1000); throw new RebusApplicationException(exception, $"Unexpected exception thrown while trying to dequeue a message from rabbitmq, queue address: {Address}"); } }
public async Task<TransportMessage> Receive(ITransactionContext context) { using (await _bottleneck.Enter()) { var brokeredMessage = await ReceiveBrokeredMessage(); if (brokeredMessage == null) return null; var headers = brokeredMessage.Properties .Where(kvp => kvp.Value is string) .ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value); var messageId = headers.GetValueOrNull(Headers.MessageId); _log.Debug("Received brokered message with ID {0}", messageId); var leaseDuration = (brokeredMessage.LockedUntilUtc - DateTime.UtcNow); var lockRenewalInterval = TimeSpan.FromMinutes(0.8 * leaseDuration.TotalMinutes); var renewalTask = GetRenewalTaskOrFakeDisposable(messageId, brokeredMessage, lockRenewalInterval); context.OnAborted(() => { renewalTask.Dispose(); _log.Debug("Abandoning message with ID {0}", messageId); try { brokeredMessage.Abandon(); } catch (Exception exception) { // if it fails, it'll be back on the queue anyway.... _log.Warn("Could not abandon message: {0}", exception); } }); context.OnCommitted(async () => { renewalTask.Dispose(); }); context.OnCompleted(async () => { _log.Debug("Completing message with ID {0}", messageId); await GetRetrier().Execute(() => brokeredMessage.CompleteAsync()); }); context.OnDisposed(() => { renewalTask.Dispose(); _log.Debug("Disposing message with ID {0}", messageId); brokeredMessage.Dispose(); }); return new TransportMessage(headers, brokeredMessage.GetBody<byte[]>()); } }
public async Task<TransportMessage> Receive(ITransactionContext context) { if (_inputQueueAddress == null) { throw new InvalidOperationException("This Azure Service Bus transport does not have an input queue, hence it is not possible to reveive anything"); } using (await _bottleneck.Enter()) { var brokeredMessage = await ReceiveBrokeredMessage(); if (brokeredMessage == null) return null; var headers = brokeredMessage.Properties .Where(kvp => kvp.Value is string) .ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value); var messageId = headers.GetValueOrNull(Headers.MessageId); var leaseDuration = (brokeredMessage.LockedUntilUtc - DateTime.UtcNow); var lockRenewalInterval = TimeSpan.FromMinutes(0.8 * leaseDuration.TotalMinutes); var renewalTask = GetRenewalTaskOrFakeDisposable(messageId, brokeredMessage, lockRenewalInterval); context.OnAborted(() => { renewalTask.Dispose(); try { brokeredMessage.Abandon(); } catch (Exception exception) { // if it fails, it'll be back on the queue anyway.... _log.Warn("Could not abandon message: {0}", exception); } }); context.OnCommitted(async () => renewalTask.Dispose()); context.OnCompleted(async () => { await brokeredMessage.CompleteAsync(); }); context.OnDisposed(() => { renewalTask.Dispose(); brokeredMessage.Dispose(); }); using (var memoryStream = new MemoryStream()) { await brokeredMessage.GetBody<Stream>().CopyToAsync(memoryStream); return new TransportMessage(headers, memoryStream.ToArray()); } } }
/// <summary> /// Receives the next message (if any) from the transport's input queue <see cref="ITransport.Address"/> /// </summary> public async Task<TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken = default(CancellationToken)) { if (_inputQueueName == null) { throw new InvalidOperationException("This Azure Storage Queues transport does not have an input queue, hence it is not possible to reveive anything"); } var inputQueue = GetQueue(_inputQueueName); var cloudQueueMessage = await inputQueue.GetMessageAsync(_initialVisibilityDelay, new QueueRequestOptions(), new OperationContext(), cancellationToken); if (cloudQueueMessage == null) return null; context.OnCompleted(async () => { // if we get this far, don't pass on the cancellation token await inputQueue.DeleteMessageAsync(cloudQueueMessage); }); context.OnAborted(() => { inputQueue.UpdateMessage(cloudQueueMessage, TimeSpan.FromSeconds(0), MessageUpdateFields.Visibility); }); return Deserialize(cloudQueueMessage); }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, string address, CancellationToken cancellationToken) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (address == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to receive anything"); } var queueUrl = m_amazonSqsQueueContext.GetDestinationQueueUrlByName(address, context); if (string.IsNullOrWhiteSpace(queueUrl)) { throw new InvalidOperationException("The queue URL is empty - has the transport not been initialized?"); } var client = m_amazonInternalSettings.CreateSqsClient(context); var request = new ReceiveMessageRequest(queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = m_amazonInternalSettings.AmazonSnsAndSqsTransportOptions.ReceiveWaitTimeSeconds, AttributeNames = new List <string>(new[] { "All" }), MessageAttributeNames = new List <string>(new[] { "All" }) }; var response = await client.ReceiveMessageAsync(request, cancellationToken); if (response.Messages.Any() == false) { return(null); } var sqsMessage = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(sqsMessage, queueUrl, client); context.OnCompleted(async() => { renewalTask.Dispose(); // if we get this far, we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(queueUrl, sqsMessage.ReceiptHandle)); }); context.OnAborted(() => { renewalTask.Dispose(); Task.Run(() => client.ChangeMessageVisibilityAsync(queueUrl, sqsMessage.ReceiptHandle, 0, cancellationToken), cancellationToken).Wait(cancellationToken); }); IAmazonMessageProcessor amazonMessageProcessor = _amazonMessageProcessorFactory.Create(sqsMessage); var transportMessage = amazonMessageProcessor.ProcessMessage(); if (transportMessage.MessageIsExpired(sqsMessage)) { // if the message is expired , we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(queueUrl, sqsMessage.ReceiptHandle)); return(null); } renewalTask.Start(); return(transportMessage); }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue - therefore, it is not possible to reveive anything"); } try { EnsureConsumerInitialized(); var consumer = _consumer; // initialization must have failed if (consumer == null) { return(null); } var model = consumer.Model; if (!model.IsOpen) { // something is wrong - we would not be able to ACK messages - force re-initialization to happen _consumer = null; // try to get rid of the consumer we have here try { model.Dispose(); } catch { } } BasicDeliverEventArgs result; if (!consumer.Queue.Dequeue(TwoSeconds, out result)) { return(null); } var deliveryTag = result.DeliveryTag; context.OnCommitted(async() => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { // we might not be able to do this, but it doesn't matter that much if it succeeds try { model.BasicNack(deliveryTag, false, true); } catch { } }); return(CreateTransportMessage(result.BasicProperties, result.Body)); } catch (EndOfStreamException exception) { ClearConsumer(); throw new RebusApplicationException(exception, "Queue throw EndOfStreamException(meaning it was canceled by rabbitmq)"); } catch (Exception exception) { ClearConsumer(); Thread.Sleep(1000); throw new RebusApplicationException(exception, $"unexpected exception thrown while trying to dequeue a message from rabbitmq, queue address: {Address}"); } }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (Address == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to reveive anything"); } if (string.IsNullOrWhiteSpace(_queueUrl)) { throw new InvalidOperationException("The queue URL is empty - has the transport not been initialized?"); } var client = GetClientFromTransactionContext(context); var request = new ReceiveMessageRequest(_queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = _options.ReceiveWaitTimeSeconds, AttributeNames = new List <string>(new[] { "All" }), MessageAttributeNames = new List <string>(new[] { "All" }) }; var response = await client.ReceiveMessageAsync(request, cancellationToken); if (!response.Messages.Any()) { return(null); } var sqsMessage = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(sqsMessage, client); context.OnCompleted(async() => { renewalTask.Dispose(); // if we get this far, we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, sqsMessage.ReceiptHandle)); }); context.OnAborted(() => { renewalTask.Dispose(); Task.Run(() => client.ChangeMessageVisibilityAsync(_queueUrl, sqsMessage.ReceiptHandle, 0, cancellationToken), cancellationToken).Wait(cancellationToken); }); var transportMessage = ExtractTransportMessageFrom(sqsMessage); if (MessageIsExpired(transportMessage, sqsMessage)) { // if the message is expired , we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, sqsMessage.ReceiptHandle)); return(null); } renewalTask.Start(); return(transportMessage); }
/// <summary> /// Receives the next message from the input queue. Returns null if no message was available /// </summary> public async Task <TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken) { var receivedMessage = await ReceiveInternal().ConfigureAwait(false); if (receivedMessage == null) { return(null); } var message = receivedMessage.Message; var messageReceiver = receivedMessage.MessageReceiver; if (!message.SystemProperties.IsLockTokenSet) { throw new RebusApplicationException($"OMG that's weird - message with ID {message.MessageId} does not have a lock token!"); } var lockToken = message.SystemProperties.LockToken; var messageId = message.MessageId; if (AutomaticallyRenewPeekLock && !_prefetchingEnabled) { var now = DateTime.UtcNow; var leaseDuration = message.SystemProperties.LockedUntilUtc - now; var lockRenewalInterval = TimeSpan.FromMinutes(0.5 * leaseDuration.TotalMinutes); var cancellationTokenSource = new CancellationTokenSource(); var renewalTask = _asyncTaskFactory .Create(description: $"RenewPeekLock-{messageId}", action: () => RenewPeekLock(messageReceiver, messageId, lockToken, cancellationTokenSource), intervalSeconds: (int)lockRenewalInterval.TotalSeconds, prettyInsignificant: true ); // be sure to stop the renewal task regardless of whether we're committing or aborting context.OnCommitted(async() => renewalTask.Dispose()); context.OnAborted(() => renewalTask.Dispose()); context.OnDisposed(() => { renewalTask.Dispose(); cancellationTokenSource.Dispose(); }); cancellationTokenSource.Token.Register(renewalTask.Dispose); renewalTask.Start(); } context.OnCompleted(async() => { try { await messageReceiver.CompleteAsync(lockToken).ConfigureAwait(false); } catch (Exception exception) { throw new RebusApplicationException(exception, $"Could not complete message with ID {message.MessageId} and lock token {lockToken}"); } }); context.OnAborted(() => { try { AsyncHelpers.RunSync(async() => await messageReceiver.AbandonAsync(lockToken).ConfigureAwait(false)); } catch (Exception exception) { throw new RebusApplicationException(exception, $"Could not abandon message with ID {message.MessageId} and lock token {lockToken}"); } }); var userProperties = message.UserProperties; var headers = userProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString()); var body = message.Body; return(new TransportMessage(headers, body)); }
public async Task<TransportMessage> Receive(ITransactionContext context) { var model = GetModel(context); var result = model.BasicGet(Address, false); if (result == null) return null; var deliveryTag = result.DeliveryTag; context.OnCompleted(async () => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { model.BasicNack(deliveryTag, false, true); }); var headers = result.BasicProperties.Headers .ToDictionary(kvp => kvp.Key, kvp => { var headerValue = kvp.Value; if (headerValue is byte[]) { var stringHeaderValue = HeaderValueEncoding.GetString((byte[])headerValue); return stringHeaderValue; } return headerValue.ToString(); }); return new TransportMessage(headers, result.Body); }
public Task Send(string destinationAddress, TransportMessage message, ITransactionContext context) { if (destinationAddress == null) throw new ArgumentNullException("destinationAddress"); if (message == null) throw new ArgumentNullException("message"); if (context == null) throw new ArgumentNullException("context"); var outputQueue = context.GetOrAdd(OutgoingQueueContextKey, () => new InMemOutputQueue()); var contextActionsSet = context.GetOrAdd(OutgoingQueueContextActionIsSetKey, () => false); if (!contextActionsSet) { context.OnCommitted(async () => { var client = GetClientFromTransactionContext(context); var messageSendRequests = outputQueue.GetMessages(); var tasks = messageSendRequests.Select(r => client.SendMessageBatchAsync(new SendMessageBatchRequest(r.DestinationAddressUrl, r.Messages.ToList()))); var response = await Task.WhenAll(tasks); if (response.Any(r => r.Failed.Any())) { GenerateErrorsAndThrow(response); } }); context.OnAborted(outputQueue.Clear); context.Items[OutgoingQueueContextActionIsSetKey] = true; } var sendMessageRequest = new SendMessageBatchRequestEntry() { MessageAttributes = CreateAttributesFromHeaders(message.Headers), MessageBody = GetBody(message.Body), Id = message.Headers.GetValueOrNull(Headers.MessageId) ?? Guid.NewGuid().ToString(), }; outputQueue.AddMessage(GetDestinationQueueUrlByName(destinationAddress, context), sendMessageRequest); return _emptyTask; }
public async Task<TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken = default(CancellationToken)) { if (context == null) throw new ArgumentNullException(nameof(context)); if (Address == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to reveive anything"); } var client = GetClientFromTransactionContext(context); var request = new ReceiveMessageRequest(_queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = 1, AttributeNames = new List<string>(new[] { "All" }), MessageAttributeNames = new List<string>(new[] { "All" }) }; var response = await client.ReceiveMessageAsync(request, cancellationToken); if (!response.Messages.Any()) return null; var message = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(message, client); context.OnCompleted(async () => { renewalTask.Dispose(); // if we get this far, we don't want to pass on the cancellation token await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); }); context.OnAborted(() => { renewalTask.Dispose(); client.ChangeMessageVisibility(_queueUrl, message.ReceiptHandle, 0); }); if (MessageIsExpired(message)) { // if the message is expired , we don't want to pass on the cancellation token await client.DeleteMessageAsync(new DeleteMessageRequest(_queueUrl, message.ReceiptHandle)); return null; } renewalTask.Start(); var transportMessage = GetTransportMessage(message); return transportMessage; }
public async Task<TransportMessage> Receive(ITransactionContext context, CancellationToken cancellationToken = default(CancellationToken)) { if (Address == null) { throw new InvalidOperationException("This RabbitMQ transport does not have an input queue - therefore, it is not possible to reveive anything"); } try { EnsureConsumerInitialized(); var consumer = _consumer; // initialization must have failed if (consumer == null) return null; var model = consumer.Model; if (!model.IsOpen) { // something is wrong - we would not be able to ACK messages - force re-initialization to happen _consumer = null; // try to get rid of the consumer we have here try { model.Dispose(); } catch { } } BasicDeliverEventArgs result; if (!consumer.Queue.Dequeue(TwoSeconds, out result)) return null; var deliveryTag = result.DeliveryTag; context.OnCommitted(async () => { model.BasicAck(deliveryTag, false); }); context.OnAborted(() => { // we might not be able to do this, but it doesn't matter that much if it succeeds try { model.BasicNack(deliveryTag, false, true); } catch { } }); return CreateTransportMessage(result); } catch (EndOfStreamException exception) { ClearConsumer(); throw new RebusApplicationException(exception, "Queue throw EndOfStreamException(meaning it was canceled by rabbitmq)"); } catch (Exception exception) { ClearConsumer(); Thread.Sleep(1000); throw new RebusApplicationException(exception, $"unexpected exception thrown while trying to dequeue a message from rabbitmq, queue address: {Address}"); } }