Example #1
0
    async def send_message_async(self, messages, close_on_done=False):
        """Send a single message or batched message asynchronously.

        :param messages: A message to send. This can either be a single instance
         of ~uamqp.message.Message, or multiple messages wrapped in an instance
         of ~uamqp.message.BatchMessage.
        :type message: ~uamqp.message.Message
        :param close_on_done: Close the client once the message is sent. Default is `False`.
        :type close_on_done: bool
        :raises: ~uamqp.errors.MessageException if message fails to send after retry policy
         is exhausted.
        """
        batch = messages.gather()
        pending_batch = []
        for message in batch:
            message.idle_time = self._counter.get_current_ms()
            async with self._pending_messages_lock:
                self._pending_messages.append(message)
            pending_batch.append(message)
        await self.open_async()
        try:
            while any([m for m in pending_batch if m.state not in constants.DONE_STATES]):
                await self.do_work_async()
            failed = [m for m in pending_batch if m.state == constants.MessageState.SendFailed]
            if any(failed):
                details = {"total_messages": len(pending_batch), "number_failed": len(failed)}
                details['failed_messages'] = {}
                exception = None
                for failed_message in failed:
                    exception = failed_message._response  # pylint: disable=protected-access
                    details['failed_messages'][failed_message] = exception
                raise errors.ClientMessageError(exception, info=details)
        finally:
            if close_on_done:
                await self.close_async()
Example #2
0
    def _on_message_sent(self, message, result, delivery_state=None):
        """Callback run on a message send operation. If message
        has a user defined callback, it will be called here. If the result
        of the operation is failure, the message state will be reverted
        to 'pending' up to the maximum retry count.

        :param message: The message that was sent.
        :type message: ~uamqp.message.Message
        :param result: The result of the send operation.
        :type result: int
        :param error: An Exception if an error ocurred during the send operation.
        :type error: ~Exception
        """
        # pylint: disable=protected-access
        exception = delivery_state
        result = constants.MessageSendResult(result)
        if result == constants.MessageSendResult.Error:
            if isinstance(delivery_state, Exception):
                exception = errors.ClientMessageError(delivery_state,
                                                      info=delivery_state)
                exception.action = errors.ErrorAction(retry=True)
            elif delivery_state:
                error = errors.ErrorResponse(delivery_state)
                exception = errors._process_send_error(self._error_policy,
                                                       error.condition,
                                                       error.description,
                                                       error.info)
            else:
                exception = errors.MessageSendFailed(
                    constants.ErrorCodes.UnknownError)
                exception.action = errors.ErrorAction(retry=True)
            if exception.action.retry == errors.ErrorAction.retry and message.retries < self._error_policy.max_retries:
                if exception.action.increment_retries:
                    message.retries += 1
                self._backoff = exception.action.backoff
                _logger.debug(
                    "Message error, retrying. Attempts: %r, Error: %r",
                    message.retries, exception)
                message.state = constants.MessageState.WaitingToBeSent
                return
            if exception.action.retry == errors.ErrorAction.retry:
                _logger.info("Message error, %r retries exhausted. Error: %r",
                             message.retries, exception)
            else:
                _logger.info("Message error, not retrying. Error: %r",
                             exception)
            message.state = constants.MessageState.SendFailed
            message._response = exception

        else:
            _logger.debug("Message sent: %r, %r", result, exception)
            message.state = constants.MessageState.SendComplete
            message._response = errors.MessageAlreadySettled()
        if message.on_send_complete:
            message.on_send_complete(result, exception)