/// <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));
        }
예제 #2
0
        /// <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));
        }
예제 #3
0
        /// <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");
                }
            }
                );
        }
예제 #5
0
        /// <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;
        }
예제 #6
0
        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);
            }
예제 #8
0
        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);
        }
예제 #9
0
        /// <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();
        }
예제 #10
0
        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();
                }
            }
                );
        }
예제 #12
0
        /// <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;
        }
예제 #13
0
    /// <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 { }
                });
            });
        }
예제 #15
0
        /// <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");
                }
            }
                );
        }
예제 #16
0
        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));
        }
예제 #17
0
        /// <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);
        }
예제 #18
0
        /// <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));
        }
예제 #19
0
        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);
        }
예제 #20
0
        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);
        }
예제 #21
0
        /// <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);
        }
예제 #22
0
        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;

        }
예제 #23
0
        /// <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()));
                }
            }
        }
예제 #25
0
        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);
        }
예제 #26
0
        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[]>()));
            }
        }
예제 #27
0
        /// <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));
        }
예제 #28
0
        /// <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}");
            }
        }
예제 #29
0
        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[]>());
            }
        }
예제 #30
0
        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);
        }
예제 #32
0
        /// <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}");
            }
        }
예제 #34
0
        /// <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));
        }
예제 #36
0
        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);
        }
예제 #37
0
        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;
        }
예제 #38
0
        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;
        }
예제 #39
0
        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}");
            }
        }