Example #1
0
        /// <summary>
        /// Rollbacks the specified message by setting the status
        /// </summary>
        /// <param name="context">The context.</param>
        public void Rollback(IMessageContext context)
        {
            var connection = context.Get(_headers.Connection);

            if (connection?.IsDisposed == false && connection?.Connection != null && connection.Transaction != null)
            {
                RollbackForTransaction(context);
                return;
            }

            if (context.MessageId == null || !context.MessageId.HasValue)
            {
                return;
            }

            //there is nothing to rollback unless at least one of these options is enabled
            if (_configuration.Options().EnableDelayedProcessing ||
                _configuration.Options().EnableHeartBeat ||
                _configuration.Options().EnableStatus)
            {
                DateTime?lastHeartBeat = null;
                if (context.WorkerNotification?.HeartBeat?.Status?.LastHeartBeatTime != null)
                {
                    lastHeartBeat = context.WorkerNotification.HeartBeat.Status.LastHeartBeatTime.Value;
                }

                var increaseDelay = context.Get(_increaseQueueDelay.QueueDelay).IncreaseDelay;
                _rollbackCommand.Handle(new RollbackMessageCommand <long>(lastHeartBeat,
                                                                          (long)context.MessageId.Id.Value, increaseDelay));
            }
        }
        /// <summary>
        /// Returns the next message, if any.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="connectionHolder">The connection.</param>
        /// <param name="noMessageFoundActon">The no message found action.</param>
        /// <returns>
        /// A message if one is found; null otherwise
        /// </returns>
        public IReceivedMessageInternal GetMessage(IMessageContext context, IConnectionHolder <SqlConnection, SqlTransaction, SqlCommand> connectionHolder,
                                                   Action <IConnectionHolder <SqlConnection, SqlTransaction, SqlCommand> > noMessageFoundActon)
        {
            //if stopping, exit now
            if (_cancelToken.Tokens.Any(t => t.IsCancellationRequested))
            {
                noMessageFoundActon(connectionHolder);
                return(null);
            }

            //check for a specific MessageID to pull
            IMessageId messageId = null;
            var        rpc       = context.Get(_configuration.HeaderNames.StandardHeaders.RpcContext);

            if (rpc?.MessageId != null && rpc.MessageId.HasValue)
            {
                messageId = rpc.MessageId;
            }

            //ask for the next message, or a specific message if we have a messageID
            var receivedTransportMessage =
                _receiveMessage.Handle(new ReceiveMessageQuery <SqlConnection, SqlTransaction>(connectionHolder.Connection,
                                                                                               connectionHolder.Transaction, messageId, _configuration.Routes));

            //if no message (null) run the no message action and return
            if (receivedTransportMessage == null)
            {
                noMessageFoundActon(connectionHolder);
                return(null);
            }

            //set the message ID on the context for later usage
            context.MessageId = receivedTransportMessage.MessageId;

            //if we are holding open transactions, we need to update the status table in a separate call
            //When not using held transactions, this is part of the de-queue statement and so not needed here

            //TODO - we could consider using a task to update the status table
            //the status table drives nothing internally, however it may drive external processes
            //because of that, we are not returning the message until the status table is updated.
            //we could make this a configurable option in the future?
            if (_configuration.Options().EnableHoldTransactionUntilMessageCommitted&&
                _configuration.Options().EnableStatusTable)
            {
                _setStatusCommandHandler.Handle(
                    new SetStatusTableStatusCommand(
                        (long)receivedTransportMessage.MessageId.Id.Value, QueueStatuses.Processing));
            }
            return(receivedTransportMessage);
        }
        /// <inheritdoc />
        public RemoveMessageStatus Remove(IMessageId id, RemoveMessageReason reason)
        {
            if (_configuration.Options().EnableHoldTransactionUntilMessageCommitted&& reason == RemoveMessageReason.Complete)
            {
                throw new DotNetWorkQueueException("Cannot use a transaction without the message context");
            }

            if (id == null || !id.HasValue)
            {
                return(RemoveMessageStatus.NotFound);
            }

            var count = _deleteMessageCommand.Handle(new DeleteMessageCommand <long>((long)id.Id.Value));

            return(count > 0 ? RemoveMessageStatus.Removed : RemoveMessageStatus.NotFound);
        }
Example #4
0
        /// <summary>
        /// Performs pre-checks on context
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private bool ReceiveSharedLogic(IMessageContext context)
        {
            if (!LoggedMissingDb && !_databaseExists.Exists(_configuration.TransportConfiguration.ConnectionInfo.ConnectionString))
            {
                _log.WarnFormat("Database file {0} does not exist", _getFileNameFromConnection.GetFileName(_configuration.TransportConfiguration.ConnectionInfo.ConnectionString).FileName);
                LoggedMissingDb = true;
            }

            if (_cancelWork.Tokens.Any(m => m.IsCancellationRequested))
            {
                return(false);
            }

            if (_configuration.Options().QueueType == QueueTypes.RpcReceive)
            {
                var rpc = context.Get(_configuration.HeaderNames.StandardHeaders.RpcContext);
                if (rpc.MessageId == null || !rpc.MessageId.HasValue)
                {
                    return(false);
                }
            }

            SetActionsOnContext(context);
            return(true);
        }
Example #5
0
        /// <summary>
        /// Returns the next message, if any.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="connectionHolder">The connection.</param>
        /// <param name="noMessageFoundActon">The no message found action.</param>
        /// <param name="routes">The routes.</param>
        /// <returns>
        /// A message if one is found; null otherwise
        /// </returns>
        public IReceivedMessageInternal GetMessage(IMessageContext context, IConnectionHolder <NpgsqlConnection, NpgsqlTransaction, NpgsqlCommand> connectionHolder,
                                                   Action <IConnectionHolder <NpgsqlConnection, NpgsqlTransaction, NpgsqlCommand> > noMessageFoundActon, List <string> routes)
        {
            //if stopping, exit now
            if (_cancelToken.Tokens.Any(t => t.IsCancellationRequested))
            {
                noMessageFoundActon(connectionHolder);
                return(null);
            }

            //check for a specific MessageID to pull
            IMessageId messageId = null;
            var        rpc       = context.Get(_configuration.HeaderNames.StandardHeaders.RpcContext);

            if (rpc?.MessageId != null && rpc.MessageId.HasValue)
            {
                messageId = rpc.MessageId;
            }

            //ask for the next message, or a specific message if we have a messageID
            var receivedTransportMessage =
                _receiveMessage.Handle(new ReceiveMessageQuery <NpgsqlConnection, NpgsqlTransaction>(connectionHolder.Connection,
                                                                                                     connectionHolder.Transaction, messageId, routes));

            //if no message (null) run the no message action and return
            if (receivedTransportMessage == null)
            {
                noMessageFoundActon(connectionHolder);
                return(null);
            }

            //set the message ID on the context for later usage
            context.MessageId = receivedTransportMessage.MessageId;

            //we need to update the status table here, as we don't do it as part of the de-queue
            if (_configuration.Options().EnableStatusTable)
            {
                _setStatusCommandHandler.Handle(
                    new SetStatusTableStatusCommand(
                        (long)receivedTransportMessage.MessageId.Id.Value, QueueStatuses.Processing));
            }
            return(receivedTransportMessage);
        }
        /// <summary>
        /// Returns a message to process.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// A message to process or null if there are no messages to process
        /// </returns>
        /// <exception cref="ReceiveMessageException">An error occurred while attempting to read messages from the queue</exception>
        public IReceivedMessageInternal ReceiveMessage(IMessageContext context)
        {
            if (_configuration.Options().EnableHoldTransactionUntilMessageCommitted)
            {
                _commitConnection   = (c, b) => _handleMessage.CommitMessage.Commit(context);
                _rollbackConnection = (c, b) => _handleMessage.RollbackMessage.Rollback(context);
            }

            try
            {
                if (_cancelWork.Tokens.Any(m => m.IsCancellationRequested))
                {
                    return(null);
                }

                var connection = GetConnectionAndSetOnContext(context);
                try
                {
                    return(_receiveMessages.GetMessage(context, connection, connection1 => _disposeConnection(connection)));
                }
                finally
                {
                    if (!_configuration.Options().EnableHoldTransactionUntilMessageCommitted)
                    {
                        _disposeConnection(connection);
                    }
                }
            }
            catch (PoisonMessageException exception)
            {
                if (exception.MessageId != null && exception.MessageId.HasValue)
                {
                    context.SetMessageAndHeaders(exception.MessageId, context.Headers);
                }
                throw;
            }
            catch (Exception exception)
            {
                throw new ReceiveMessageException("An error occurred while attempting to read messages from the queue",
                                                  exception);
            }
        }
Example #7
0
        /// <summary>
        /// Rollbacks the specified message by rolling back the transaction
        /// </summary>
        /// <param name="context">The context.</param>
        public void RollbackForTransaction(IMessageContext context)
        {
            var connection = context.Get(_headers.Connection);

            //if transaction open, then just rollback the transaction
            if (connection.Connection == null || connection.Transaction == null)
            {
                return;
            }

            if (_configuration.Options().EnableStatusTable)
            {
                if (context.MessageId != null && context.MessageId.HasValue)
                {
                    _setStatusCommandHandler.Handle(new SetStatusTableStatusCommand((long)context.MessageId.Id.Value,
                                                                                    QueueStatuses.Waiting));
                }
            }
            connection.Transaction.Rollback();
        }
        /// <summary>
        /// Rollbacks the specified message by setting the status
        /// </summary>
        /// <param name="context">The context.</param>
        public void Rollback(IMessageContext context)
        {
            if (context.MessageId == null || !context.MessageId.HasValue)
            {
                return;
            }

            //there is nothing to rollback unless at least one of these options is enabled
            if (_configuration.Options().EnableDelayedProcessing ||
                _configuration.Options().EnableHeartBeat ||
                _configuration.Options().EnableStatus)
            {
                DateTime?lastHeartBeat = null;
                if (context.WorkerNotification?.HeartBeat?.Status?.LastHeartBeatTime != null)
                {
                    lastHeartBeat = context.WorkerNotification.HeartBeat.Status.LastHeartBeatTime.Value;
                }

                var increaseDelay = context.Get(_headers.QueueDelay).IncreaseDelay;
                _rollbackCommand.Handle(new RollbackMessageCommand <int>(lastHeartBeat,
                                                                         Convert.ToInt32(context.MessageId.Id.Value), increaseDelay));
            }
        }
        /// <summary>
        /// Rollbacks the specified message by rolling back the transaction
        /// </summary>
        /// <param name="context">The context.</param>
        public void RollbackForTransaction(IMessageContext context)
        {
            var connection = context.Get(_headers.Connection);

            //if transaction open, then just rollback the transaction
            if (connection.Connection == null || connection.Transaction == null)
            {
                return;
            }

            if (_configuration.Options().EnableStatusTable)
            {
                if (context.MessageId != null && context.MessageId.HasValue)
                {
                    _setStatusCommandHandler.Handle(new SetStatusTableStatusCommand((long)context.MessageId.Id.Value,
                                                                                    QueueStatuses.Waiting));
                }
            }
            try
            {
                connection.Transaction.Rollback();
            }
            catch (Exception e)
            {
                _log.ErrorException("Failed to rollback a transaction; this might be due to a DB timeout", e);

                //don't attempt to use the transaction again at this point.
                connection.Transaction = null;

                throw;
            }

            //ensure that transaction won't be used anymore
            connection.Transaction.Dispose();
            connection.Transaction = null;
        }
Example #10
0
        /// <summary>
        /// Commits the message, via the held transaction
        /// </summary>
        /// <param name="context">The context.</param>
        public void CommitForTransaction(IMessageContext context)
        {
            var connection = context.Get(_headers.Connection);

            //if transaction held
            if (connection.Connection == null || connection.Transaction == null)
            {
                return;
            }

            //delete the message, and then commit the transaction
            _deleteTransactionalMessageCommand.Handle(new DeleteTransactionalMessageCommand((long)context.MessageId.Id.Value, context));
            connection.Transaction.Commit();
            connection.Transaction = null;

            if (_configuration.Options().EnableStatusTable)
            {
                _deleteStatusCommandHandler.Handle(new DeleteStatusTableStatusCommand((long)context.MessageId.Id.Value));
            }
        }
Example #11
0
        private IReceivedMessageInternal ProcessMessage(IReceivedMessageInternal receivedTransportMessage,
                                                        IConnectionHolder <NpgsqlConnection, NpgsqlTransaction, NpgsqlCommand> connectionHolder,
                                                        IMessageContext context,
                                                        Action <IConnectionHolder <NpgsqlConnection, NpgsqlTransaction, NpgsqlCommand> > noMessageFoundActon)
        {
            //if no message (null) run the no message action and return
            if (receivedTransportMessage == null)
            {
                noMessageFoundActon(connectionHolder);
                return(null);
            }

            //set the message ID on the context for later usage
            context.SetMessageAndHeaders(receivedTransportMessage.MessageId, receivedTransportMessage.Headers);

            //we need to update the status table here, as we don't do it as part of the de-queue
            if (_configuration.Options().EnableStatusTable)
            {
                _setStatusCommandHandler.Handle(
                    new SetStatusTableStatusCommand <long>(
                        (long)receivedTransportMessage.MessageId.Id.Value, QueueStatuses.Processing));
            }
            return(receivedTransportMessage);
        }
        /// <summary>
        /// Commits the message, via the held transaction
        /// </summary>
        /// <param name="context">The context.</param>
        public void CommitForTransaction(IMessageContext context)
        {
            var connection = context.Get(_headers.Connection);

            //if transaction held
            if (connection.Connection == null || connection.Transaction == null)
            {
                return;
            }

            //delete the message, and then commit the transaction
            _deleteTransactionalMessageCommand.Handle(new DeleteTransactionalMessageCommand((long)context.MessageId.Id.Value, context));

            try
            {
                connection.Transaction.Commit();
            }
            catch (Exception e)
            {
                _log.ErrorException("Failed to commit a transaction; this might be due to a DB timeout", e);

                //don't attempt to use the transaction again at this point.
                connection.Transaction = null;

                throw;
            }

            //ensure that transaction won't be used anymore
            connection.Transaction.Dispose();
            connection.Transaction = null;

            if (_configuration.Options().EnableStatusTable)
            {
                _deleteStatusCommandHandler.Handle(new DeleteStatusTableStatusCommand((long)context.MessageId.Id.Value));
            }
        }