Пример #1
0
    async def receive_deferred_messages(self, sequence_numbers):
        # type: (Union[int, List[int]]) -> List[ReceivedMessage]
        """Receive messages that have previously been deferred.

        When receiving deferred messages from a partitioned entity, all of the supplied
        sequence numbers must be messages from the same partition.

        :param Union[int, list[int]] sequence_numbers: A list of the sequence numbers of messages that have been
         deferred.
        :rtype: list[~azure.servicebus.aio.ReceivedMessage]

        .. admonition:: Example:

            .. literalinclude:: ../samples/async_samples/sample_code_servicebus_async.py
                :start-after: [START receive_defer_async]
                :end-before: [END receive_defer_async]
                :language: python
                :dedent: 4
                :caption: Receive deferred messages from ServiceBus.

        """
        self._check_live()
        if isinstance(sequence_numbers, six.integer_types):
            sequence_numbers = [sequence_numbers]
        if not sequence_numbers:
            raise ValueError("At least one sequence number must be specified.")
        await self._open()
        try:
            receive_mode = self._receive_mode.value.value
        except AttributeError:
            receive_mode = int(self._receive_mode)
        message = {
            MGMT_REQUEST_SEQUENCE_NUMBERS: types.AMQPArray([types.AMQPLong(s) for s in sequence_numbers]),
            MGMT_REQUEST_RECEIVER_SETTLE_MODE: types.AMQPuInt(receive_mode)
        }

        self._populate_message_properties(message)

        handler = functools.partial(mgmt_handlers.deferred_message_op,
                                    receive_mode=self._receive_mode,
                                    message_type=ReceivedMessage,
                                    receiver=self)
        messages = await self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER,
            message,
            handler
        )
        return messages
Пример #2
0
    async def receive_deferred_messages(self,
                                        sequence_numbers,
                                        mode=ReceiveSettleMode.PeekLock):
        """Receive messages that have previously been deferred.

        This operation can only receive deferred messages from the current session.
        When receiving deferred messages from a partitioned entity, all of the supplied
        sequence numbers must be messages from the same partition.

        :param sequence_numbers: A list of the sequence numbers of messages that have been
         deferred.
        :type sequence_numbers: list[int]
        :param mode: The receive mode, default value is PeekLock.
        :type mode: ~azure.servicebus.common.constants.ReceiveSettleMode
        :rtype: list[~azure.servicebus.aio.async_message.DeferredMessage]

        .. admonition:: Example:
            .. literalinclude:: ../samples/async_samples/test_examples_async.py
                :start-after: [START receiver_defer_session_messages]
                :end-before: [END receiver_defer_session_messages]
                :language: python
                :dedent: 8
                :caption: Defer messages, then retrieve them by sequence number.

        """
        if not sequence_numbers:
            raise ValueError("At least one sequence number must be specified.")
        await self._can_run()
        try:
            receive_mode = mode.value.value
        except AttributeError:
            receive_mode = int(mode)
        message = {
            'sequence-numbers':
            types.AMQPArray([types.AMQPLong(s) for s in sequence_numbers]),
            'receiver-settle-mode':
            types.AMQPuInt(receive_mode),
            'session-id':
            self.session_id
        }
        handler = functools.partial(mgmt_handlers.deferred_message_op,
                                    mode=receive_mode,
                                    message_type=DeferredMessage)
        messages = await self._mgmt_request_response(
            REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER, message, handler)
        for m in messages:
            m._receiver = self  # pylint: disable=protected-access
        return messages
Пример #3
0
    def _settle_message(self, settlement, lock_tokens, dead_letter_details=None):
        # type: (bytes, List[str], Optional[Dict[str, Any]]) -> Any
        message = {
            MGMT_REQUEST_DISPOSITION_STATUS: settlement,
            MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens)
        }

        self._populate_message_properties(message)
        if dead_letter_details:
            message.update(dead_letter_details)

        return self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION,
            message,
            mgmt_handlers.default
        )
    def _settle_message(self, settlement, lock_tokens, dead_letter_details=None):
        message = {
            MGMT_REQUEST_DISPOSITION_STATUS: settlement,
            MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens)
        }

        if self._session_id:
            message[MGMT_REQUEST_SESSION_ID] = self._session_id
        if dead_letter_details:
            message.update(dead_letter_details)

        return self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION,
            message,
            mgmt_handlers.default
        )
    async def _settle_message_via_mgmt_link(self,
                                            settlement,
                                            lock_tokens,
                                            dead_letter_details=None):
        message = {
            MGMT_REQUEST_DISPOSITION_STATUS: settlement,
            MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens)
        }

        self._populate_message_properties(message)
        if dead_letter_details:
            message.update(dead_letter_details)

        return await self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION, message,
            mgmt_handlers.default)
Пример #6
0
    async def receive_deferred_messages(self, sequence_numbers):
        # type: (List[int]) -> List[ReceivedMessage]
        """Receive messages that have previously been deferred.

        When receiving deferred messages from a partitioned entity, all of the supplied
        sequence numbers must be messages from the same partition.

        :param list[int] sequence_numbers: A list of the sequence numbers of messages that have been
         deferred.
        :rtype: list[~azure.servicebus.aio.ReceivedMessage]

        .. admonition:: Example:

            .. literalinclude:: ../samples/async_samples/sample_code_servicebus_async.py
                :start-after: [START receive_defer_async]
                :end-before: [END receive_defer_async]
                :language: python
                :dedent: 4
                :caption: Receive deferred messages from ServiceBus.

        """
        self._can_run()
        if not sequence_numbers:
            raise ValueError("At least one sequence number must be specified.")
        await self._open()
        try:
            receive_mode = self._mode.value.value
        except AttributeError:
            receive_mode = int(self._mode)
        message = {
            MGMT_REQUEST_SEQUENCE_NUMBERS:
            types.AMQPArray([types.AMQPLong(s) for s in sequence_numbers]),
            MGMT_REQUEST_RECEIVER_SETTLE_MODE:
            types.AMQPuInt(receive_mode)
        }

        if self._session_id:
            message[MGMT_REQUEST_SESSION_ID] = self._session_id

        handler = functools.partial(mgmt_handlers.deferred_message_op,
                                    mode=self._mode,
                                    message_type=ReceivedMessage)
        messages = await self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER, message, handler)
        for m in messages:
            m._receiver = self  # pylint: disable=protected-access
        return messages
    async def cancel_scheduled_messages(self,
                                        sequence_numbers: Union[int,
                                                                List[int]],
                                        *,
                                        timeout: Optional[float] = None,
                                        **kwargs: Any) -> None:
        """
        Cancel one or more messages that have previously been scheduled and are still pending.

        :param sequence_numbers: The sequence numbers of the scheduled messages.
        :type sequence_numbers: int or list[int]
        :keyword float timeout: The total operation timeout in seconds including all the retries. The value must be
         greater than 0 if specified. The default value is None, meaning no timeout.
        :rtype: None
        :raises: ~azure.servicebus.exceptions.ServiceBusError if messages cancellation failed due to message already
         cancelled or enqueued.

        .. admonition:: Example:

            .. literalinclude:: ../samples/async_samples/sample_code_servicebus_async.py
                :start-after: [START cancel_scheduled_messages_async]
                :end-before: [END cancel_scheduled_messages_async]
                :language: python
                :dedent: 4
                :caption: Cancelling messages scheduled to be sent in future
        """
        if kwargs:
            warnings.warn(f"Unsupported keyword args: {kwargs}")
        self._check_live()
        if timeout is not None and timeout <= 0:
            raise ValueError("The timeout must be greater than 0.")
        if isinstance(sequence_numbers, int):
            numbers = [types.AMQPLong(sequence_numbers)]
        else:
            numbers = [types.AMQPLong(s) for s in sequence_numbers]
        if len(numbers) == 0:
            return None  # no-op on empty list.
        request_body = {
            MGMT_REQUEST_SEQUENCE_NUMBERS: types.AMQPArray(numbers)
        }
        return await self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_CANCEL_SCHEDULED_MESSAGE_OPERATION,
            request_body,
            mgmt_handlers.default,
            timeout=timeout,
        )
Пример #8
0
    def _settle_message_via_mgmt_link(self, settlement, lock_tokens, dead_letter_details=None):
        # type: (str, List[Union[uuid.UUID, str]], Optional[Dict[str, Any]]) -> Any
        message = {
            MGMT_REQUEST_DISPOSITION_STATUS: settlement,
            MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens)
        }

        self._populate_message_properties(message)
        if dead_letter_details:
            message.update(dead_letter_details)

        # We don't do retry here, retry is done in the ServiceBusReceivedMessage._settle_message
        return self._mgmt_request_response(
            REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION,
            message,
            mgmt_handlers.default
        )
Пример #9
0
async def test_event_hubs_idempotent_producer(live_eventhub_config):

    uri = "sb://{}/{}".format(live_eventhub_config['hostname'],
                              live_eventhub_config['event_hub'])
    sas_auth = authentication.SASTokenAsync.from_shared_access_key(
        uri, live_eventhub_config['key_name'],
        live_eventhub_config['access_key'])

    target = "amqps://{}/{}/Partitions/0".format(
        live_eventhub_config['hostname'], live_eventhub_config['event_hub'])

    symbol_array = [
        uamqp_types.AMQPSymbol(b"com.microsoft:idempotent-producer")
    ]
    desired_capabilities = utils.data_factory(
        uamqp_types.AMQPArray(symbol_array))

    link_properties = {
        uamqp_types.AMQPSymbol(b"com.microsoft:timeout"):
        uamqp_types.AMQPLong(int(60 * 1000))
    }

    def on_attach(attach_source, attach_target, properties, error):
        if str(attach_target) == target:
            on_attach.owner_level = properties.get(
                b"com.microsoft:producer-epoch")
            on_attach.producer_group_id = properties.get(
                b"com.microsoft:producer-id")
            on_attach.starting_sequence_number = properties.get(
                b"com.microsoft:producer-sequence-number")

    send_client = uamqp.SendClientAsync(
        target,
        auth=sas_auth,
        desired_capabilities=desired_capabilities,
        link_properties=link_properties,
        on_attach=on_attach,
        debug=True)
    await send_client.open_async()
    while not await send_client.client_ready_async():
        await asyncio.sleep(0.05)

    assert on_attach.owner_level is not None
    assert on_attach.producer_group_id is not None
    assert on_attach.starting_sequence_number is not None
    await send_client.close_async()
    def receive_deferred_messages(self, sequence_numbers, mode=ReceiveSettleMode.PeekLock, **kwargs):
        """Receive messages by sequence number that have been previously deferred.

        When receiving deferred messages from a partitioned entity, all of the supplied
        sequence numbers must be messages from the same partition.

        :param sequence_numbers: A list of the sequence numbers of messages that have been
         deferred.
        :type sequence_numbers: list[int]
        :param mode: The mode with which messages will be retrieved from the entity. The two options
         are PeekLock and ReceiveAndDelete. Messages received with PeekLock must be settled within a given
         lock period before they will be removed from the queue. Messages received with ReceiveAndDelete
         will be immediately removed from the queue, and cannot be subsequently rejected or re-received if
         the client fails to process the message. The default mode is PeekLock.
        :type mode: ~azure.servicebus.common.constants.ReceiveSettleMode
        :rtype: list[~azure.servicebus.common.message.Message]

        .. admonition:: Example:
            .. literalinclude:: ../samples/sync_samples/test_examples.py
                :start-after: [START receive_deferred_messages_service_bus]
                :end-before: [END receive_deferred_messages_service_bus]
                :language: python
                :dedent: 8
                :caption: Get the messages which were deferred using their sequence numbers

        """
        if (self.entity and self.requires_session) or kwargs.get('session'):
            raise ValueError("Sessionful deferred messages can only be received within a locked receive session.")
        if not sequence_numbers:
            raise ValueError("At least one sequence number must be specified.")
        try:
            receive_mode = mode.value.value
        except AttributeError:
            receive_mode = int(mode)
        message = {
            'sequence-numbers': types.AMQPArray([types.AMQPLong(s) for s in sequence_numbers]),
            'receiver-settle-mode': types.AMQPuInt(receive_mode)}
        mgmt_handler = functools.partial(mgmt_handlers.deferred_message_op, mode=receive_mode)
        with BaseHandler(self.entity_uri, self.auth_config, debug=self.debug, **kwargs) as handler:
            return handler._mgmt_request_response(  # pylint: disable=protected-access
                REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER,
                message,
                mgmt_handler)
    async def settle_deferred_messages(self, settlement, messages, **kwargs):
        """Settle messages that have been previously deferred.

        :param settlement: How the messages are to be settled. This must be a string
         of one of the following values: 'completed', 'suspended', 'abandoned'.
        :type settlement: str
        :param messages: A list of deferred messages to be settled.
        :type messages: list[~azure.servicebus.aio.async_message.DeferredMessage]

        Example:
            .. literalinclude:: ../examples/async_examples/test_examples_async.py
                :start-after: [START client_settle_deferred_messages]
                :end-before: [END client_settle_deferred_messages]
                :language: python
                :dedent: 4
                :caption: Settle deferred messages.

        """
        if (self.entity and self.requires_session) or kwargs.get('session'):
            raise ValueError(
                "Sessionful deferred messages can only be settled within a locked receive session."
            )
        if settlement.lower() not in ['completed', 'suspended', 'abandoned']:
            raise ValueError(
                "Settlement must be one of: 'completed', 'suspended', 'abandoned'"
            )
        if not messages:
            raise ValueError("At least one message must be specified.")
        message = {
            'disposition-status': settlement.lower(),
            'lock-tokens': types.AMQPArray([m.lock_token for m in messages])
        }

        async with BaseHandler(self.entity_uri,
                               self.auth_config,
                               loop=self.loop,
                               debug=self.debug,
                               **kwargs) as handler:
            return await handler._mgmt_request_response(  # pylint: disable=protected-access
                REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION, message,
                mgmt_handlers.default)
def test_event_hubs_client_receive_with_runtime_metric_sync(
        live_eventhub_config):
    uri = "sb://{}/{}".format(live_eventhub_config['hostname'],
                              live_eventhub_config['event_hub'])
    sas_auth = authentication.SASTokenAuth.from_shared_access_key(
        uri, live_eventhub_config['key_name'],
        live_eventhub_config['access_key'])

    source = "amqps://{}/{}/ConsumerGroups/{}/Partitions/{}".format(
        live_eventhub_config['hostname'], live_eventhub_config['event_hub'],
        live_eventhub_config['consumer_group'],
        live_eventhub_config['partition'])

    receiver_runtime_metric_symbol = b'com.microsoft:enable-receiver-runtime-metric'
    symbol_array = [types.AMQPSymbol(receiver_runtime_metric_symbol)]
    desired_capabilities = utils.data_factory(types.AMQPArray(symbol_array))

    with uamqp.ReceiveClient(
            source,
            auth=sas_auth,
            debug=False,
            timeout=50,
            prefetch=50,
            desired_capabilities=desired_capabilities) as receive_client:
        log.info("Created client, receiving...")
        with pytest.raises(ValueError):
            batch = receive_client.receive_message_batch(max_batch_size=100)
        batch = receive_client.receive_message_batch(max_batch_size=10)
        log.info("Got batch: {}".format(len(batch)))
        assert len(batch) <= 10
        for message in batch:
            annotations = message.annotations
            delivery_annotations = message.delivery_annotations
            log.info("Sequence Number: {}".format(
                annotations.get(b'x-opt-sequence-number')))
            assert b'last_enqueued_sequence_number' in delivery_annotations
            assert b'last_enqueued_offset' in delivery_annotations
            assert b'last_enqueued_time_utc' in delivery_annotations
            assert b'runtime_info_retrieval_time_utc' in delivery_annotations
    log.info("Finished receiving")
    async def cancel_scheduled_messages(self, *sequence_numbers):
        """Cancel one or more messages that have previsouly been scheduled and are still pending.

        :param sequence_numbers: The seqeuence numbers of the scheduled messages.
        :type sequence_numbers: int

        Example:
            .. literalinclude:: ../examples/async_examples/test_examples_async.py
                :start-after: [START cancel_schedule_messages]
                :end-before: [END cancel_schedule_messages]
                :language: python
                :dedent: 4
                :caption: Schedule messages.

        """
        if not self.running:
            await self.open()
        numbers = [types.AMQPLong(s) for s in sequence_numbers]
        request_body = {'sequence-numbers': types.AMQPArray(numbers)}
        return await self._mgmt_request_response(
            REQUEST_RESPONSE_CANCEL_SCHEDULED_MESSAGE_OPERATION, request_body,
            mgmt_handlers.default)
Пример #14
0
    def _create_handler(self):
        source = Source(self._source)
        if self._offset is not None:
            source.set_filter(self._offset._selector())  # pylint:disable=protected-access

        if StrictVersion(uamqp.__version__) < StrictVersion(
                "1.2.3"):  # backward compatible until uamqp 1.2.3 released
            desired_capabilities = {}
        elif self._track_last_enqueued_event_properties:
            symbol_array = [
                types.AMQPSymbol(self._receiver_runtime_metric_symbol)
            ]
            desired_capabilities = {
                "desired_capabilities":
                utils.data_factory(types.AMQPArray(symbol_array))
            }
        else:
            desired_capabilities = {"desired_capabilities": None}

        self._handler = ReceiveClientAsync(
            source,
            auth=self._client._create_auth(),  # pylint:disable=protected-access
            debug=self._client._config.network_tracing,  # pylint:disable=protected-access
            prefetch=self._prefetch,
            link_properties=self._link_properties,
            timeout=self._timeout,
            error_policy=self._retry_policy,
            keep_alive_interval=self._keep_alive,
            client_name=self._name,
            receive_settle_mode=uamqp.constants.ReceiverSettleMode.
            ReceiveAndDelete,
            auto_complete=False,
            properties=self._client._create_properties(  # pylint:disable=protected-access
                self._client._config.user_agent),  # pylint:disable=protected-access
            **desired_capabilities,  # pylint:disable=protected-access
            loop=self._loop)
        self._messages_iter = None
Пример #15
0
    def _create_handler(self, auth):
        # type: (JWTTokenAuth) -> None
        source = Source(self._source)
        if self._offset is not None:
            source.set_filter(
                event_position_selector(self._offset, self._offset_inclusive)
            )
        desired_capabilities = None
        if self._track_last_enqueued_event_properties:
            symbol_array = [types.AMQPSymbol(RECEIVER_RUNTIME_METRIC_SYMBOL)]
            desired_capabilities = utils.data_factory(types.AMQPArray(symbol_array))

        properties = create_properties(
            self._client._config.user_agent  # pylint:disable=protected-access
        )
        self._handler = ReceiveClient(
            source,
            auth=auth,
            debug=self._client._config.network_tracing,  # pylint:disable=protected-access
            prefetch=self._prefetch,
            link_properties=self._link_properties,
            timeout=self._timeout,
            idle_timeout=self._idle_timeout,
            error_policy=self._retry_policy,
            keep_alive_interval=self._keep_alive,
            client_name=self._name,
            receive_settle_mode=uamqp.constants.ReceiverSettleMode.ReceiveAndDelete,
            auto_complete=False,
            properties=properties,
            desired_capabilities=desired_capabilities,
        )

        self._handler._streaming_receive = True  # pylint:disable=protected-access
        self._handler._message_received_callback = (  # pylint:disable=protected-access
            self._message_received
        )
Пример #16
0
    async def receive_deferred_messages(
        self, sequence_numbers: Union[int, List[int]], **kwargs: Any
    ) -> List[ServiceBusReceivedMessage]:
        """Receive messages that have previously been deferred.

        When receiving deferred messages from a partitioned entity, all of the supplied
        sequence numbers must be messages from the same partition.

        :param Union[int, list[int]] sequence_numbers: A list of the sequence numbers of messages that have been
         deferred.
        :keyword Optional[float] timeout: The total operation timeout in seconds including all the retries.
         The value must be greater than 0 if specified. The default value is None, meaning no timeout.
        :rtype: list[~azure.servicebus.aio.ServiceBusReceivedMessage]

        .. admonition:: Example:

            .. literalinclude:: ../samples/async_samples/sample_code_servicebus_async.py
                :start-after: [START receive_defer_async]
                :end-before: [END receive_defer_async]
                :language: python
                :dedent: 4
                :caption: Receive deferred messages from ServiceBus.

        """
        self._check_live()
        timeout = kwargs.pop("timeout", None)
        if timeout is not None and timeout <= 0:
            raise ValueError("The timeout must be greater than 0.")
        if isinstance(sequence_numbers, six.integer_types):
            sequence_numbers = [sequence_numbers]
        if len(sequence_numbers) == 0:
            return []  # no-op on empty list.
        await self._open()
        uamqp_receive_mode = ServiceBusToAMQPReceiveModeMap[self._receive_mode]
        try:
            receive_mode = uamqp_receive_mode.value.value
        except AttributeError:
            receive_mode = int(uamqp_receive_mode.value)
        message = {
            MGMT_REQUEST_SEQUENCE_NUMBERS: types.AMQPArray(
                [types.AMQPLong(s) for s in sequence_numbers]
            ),
            MGMT_REQUEST_RECEIVER_SETTLE_MODE: types.AMQPuInt(receive_mode),
        }

        self._populate_message_properties(message)

        handler = functools.partial(
            mgmt_handlers.deferred_message_op,
            receive_mode=self._receive_mode,
            message_type=ServiceBusReceivedMessage,
            receiver=self,
        )
        messages = await self._mgmt_request_response_with_retry(
            REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER,
            message,
            handler,
            timeout=timeout,
        )
        links = get_receive_links(message)
        with receive_trace_context_manager(
            self, span_name=SPAN_NAME_RECEIVE_DEFERRED, links=links
        ):
            if (
                self._auto_lock_renewer
                and not self._session
                and self._receive_mode != ServiceBusReceiveMode.RECEIVE_AND_DELETE
            ):
                for message in messages:
                    self._auto_lock_renewer.register(self, message)
            return messages
 def _renew_locks(self, *lock_tokens):
     # type: (*str) -> Any
     message = {MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens)}
     return self._mgmt_request_response_with_retry(
         REQUEST_RESPONSE_RENEWLOCK_OPERATION, message,
         mgmt_handlers.lock_renew_op)
Пример #18
0
 def _renew_locks(self, *lock_tokens):
     message = {'lock-tokens': types.AMQPArray(lock_tokens)}
     return self._mgmt_request_response(
         REQUEST_RESPONSE_RENEWLOCK_OPERATION, message,
         mgmt_handlers.lock_renew_op)