From 2e5d2637a9188bb6e0435be55f4fb5320f48958f Mon Sep 17 00:00:00 2001 From: "Adam Ling (MSFT)" Date: Mon, 2 Nov 2020 15:38:12 -0800 Subject: [PATCH] [ServiceBus] Settlement move from Message to Receiver (#14681) * code change to move settlement from message to receiver * fix mypy * update code change * more code change * sync test code update * update test code * fix mypy and pylint * fix small issue in exception and live test * fix pylint * update sample code, doc, stress test code * update changelog and migration guide * update changelog * address review comments * eliminate duplication code in settlement --- sdk/servicebus/azure-servicebus/CHANGELOG.md | 16 +- sdk/servicebus/azure-servicebus/README.md | 65 ++- .../azure/servicebus/__init__.py | 8 +- .../servicebus/_common/auto_lock_renewer.py | 23 +- .../azure/servicebus/_common/constants.py | 18 +- .../azure/servicebus/_common/message.py | 412 +++--------------- .../azure/servicebus/_common/mgmt_handlers.py | 7 +- .../servicebus/_common/receiver_mixins.py | 64 ++- .../azure/servicebus/_servicebus_receiver.py | 203 ++++++++- .../azure/servicebus/aio/__init__.py | 2 - .../aio/_async_auto_lock_renewer.py | 32 +- .../azure/servicebus/aio/_async_message.py | 190 -------- .../aio/_servicebus_client_async.py | 2 +- .../aio/_servicebus_receiver_async.py | 214 ++++++++- .../azure/servicebus/exceptions.py | 19 +- .../azure-servicebus/migration_guide.md | 6 +- .../async_samples/auto_lock_renew_async.py | 12 +- .../receive_deadlettered_messages_async.py | 4 +- .../receive_deferred_message_queue_async.py | 4 +- .../receive_iterator_queue_async.py | 2 +- .../async_samples/receive_queue_async.py | 2 +- .../receive_subscription_async.py | 2 +- .../sample_code_servicebus_async.py | 12 +- .../session_pool_receive_async.py | 4 +- .../session_send_receive_async.py | 2 +- .../samples/sync_samples/auto_lock_renew.py | 14 +- .../receive_deadlettered_messages.py | 4 +- .../receive_deferred_message_queue.py | 4 +- .../sync_samples/receive_iterator_queue.py | 2 +- .../samples/sync_samples/receive_queue.py | 2 +- .../sync_samples/receive_subscription.py | 2 +- .../sync_samples/sample_code_servicebus.py | 21 +- .../sync_samples/session_pool_receive.py | 4 +- .../sync_samples/session_send_receive.py | 2 +- .../tests/async_tests/mocks_async.py | 14 +- .../tests/async_tests/test_queues_async.py | 204 ++++----- .../tests/async_tests/test_sessions_async.py | 222 +++++----- .../async_tests/test_subscriptions_async.py | 10 +- .../azure-servicebus/tests/mocks.py | 14 +- .../tests/stress_tests/stress_test_base.py | 2 +- .../stress_test_queue_long_term_autorenew.py | 4 +- .../azure-servicebus/tests/test_queues.py | 233 +++++----- .../azure-servicebus/tests/test_sb_client.py | 2 +- .../azure-servicebus/tests/test_sessions.py | 160 ++++--- .../tests/test_subscriptions.py | 10 +- 45 files changed, 1114 insertions(+), 1141 deletions(-) delete mode 100644 sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 523009736af3..40775911ec8a 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -6,9 +6,8 @@ * Added support for `timeout` parameter on the following operations: - `ServiceBusSender`: `send_messages`, `schedule_messages` and `cancel_scheduled_messages` - - `ServiceBusReceiver`: `receive_deferred_messages` and `peek_messages` + - `ServiceBusReceiver`: `receive_deferred_messages`, `peek_messages` and `renew_message_lock` - `ServiceBusSession`: `get_state`, `set_state` and `renew_lock` - - `ReceivedMessage`: `renew_lock` * `azure.servicebus.exceptions.ServiceBusError` now inherits from `azure.core.exceptions.AzureError`. **Breaking Changes** @@ -22,10 +21,11 @@ * Removed error `azure.servicebus.exceptions.ServiceBusResourceNotFound` as `azure.core.exceptions.ResourceNotFoundError` is now raised when a Service Bus resource does not exist when using the `ServiceBusAdministrationClient`. * Renamed `Message` to `ServiceBusMessage`. -* Renamed `PeekedMessage` to `ServiceBusPeekedMessage`. * Renamed `ReceivedMessage` to `ServiceBusReceivedMessage`. * Renamed `BatchMessage` to `ServiceBusMessageBatch`. - Renamed method `add` to `add_message` on the class. +* Removed class `PeekedMessage`. +* Removed class `ReceivedMessage` under module `azure.servicebus.aio`. * Renamed `ServiceBusSender.create_batch` to `ServiceBusSender.create_message_batch`. * Removed class `ServiceBusSessionReceiver` which is now unified within class `ServiceBusReceiver`. - Removed methods `ServiceBusClient.get_queue_session_receiver` and `ServiceBusClient.get_subscription_session_receiver`. @@ -36,7 +36,15 @@ now raise more concrete exception other than `MessageSettleFailed` and `ServiceB * Exceptions `MessageSendFailed`, `MessageSettleFailed` and `MessageLockExpired` now inherit from `azure.servicebus.exceptions.MessageError`. * `get_state` in `ServiceBusSession` now returns `bytes` instead of a `string`. -* `encoding` support is removed from `ServiceBusMessage` +* Message settlement methods are moved from `ServiceBusMessage` to `ServiceBusReceiver`: + - Use `ServiceBusReceiver.complete_message` instead of `ServiceBusReceivedMessage.complete` to complete a message. + - Use `ServiceBusReceiver.abandon_message` instead of `ServiceBusReceivedMessage.abandon` to abandon a message. + - Use `ServiceBusReceiver.defer_message` instead of `ServiceBusReceivedMessage.defer` to defer a message. + - Use `ServiceBusReceiver.dead_letter_message` instead of `ServiceBusReceivedMessage.dead_letter` to dead letter a message. +* Message `renew_lock` method is moved from `ServiceBusMessage` to `ServiceBusReceiver`: + - Changed `ServiceBusReceivedMessage.renew_lock` to `ServiceBusReceiver.renew_message_lock` +* `AutoLockRenewer.register` now takes `ServiceBusReceiver` as a positional parameter. +* Removed `encoding` support from `ServiceBusMessage`. **BugFixes** diff --git a/sdk/servicebus/azure-servicebus/README.md b/sdk/servicebus/azure-servicebus/README.md index c30e7f3a71e1..f3fa9cb83c04 100644 --- a/sdk/servicebus/azure-servicebus/README.md +++ b/sdk/servicebus/azure-servicebus/README.md @@ -85,7 +85,7 @@ To interact with these resources, one should be familiar with the following SDK * [Receiver][receiver_reference]: To receive messages from a Queue or Subscription, one would use the corresponding `get_queue_receiver` or `get_subscription_receiver` method off of a `ServiceBusClient` instance as seen [here](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_queue.py). -* [Message][message_reference]: When sending, this is the type you will construct to contain your payload. When receiving, this is where you will access the payload and control how the message is "settled" (completed, dead-lettered, etc); these functions are only available on a received message. +* [Message][message_reference]: When sending, this is the type you will construct to contain your payload. When receiving, this is where you will access the payload. ## Examples @@ -151,7 +151,7 @@ with ServiceBusClient.from_connection_string(connstr) as client: ``` > **NOTE:** Any message received with `mode=PeekLock` (this is the default, with the alternative ReceiveAndDelete removing the message from the queue immediately on receipt) -> has a lock that must be renewed via `message.renew_lock()` before it expires if processing would take longer than the lock duration. +> has a lock that must be renewed via `receiver.renew_message_lock` before it expires if processing would take longer than the lock duration. > See [AutoLockRenewer](#automatically-renew-message-or-session-locks) for a helper to perform this in the background automatically. > Lock duration is set in Azure on the queue or topic itself. @@ -236,12 +236,12 @@ with ServiceBusClient.from_connection_string(connstr) as client: When receiving from a queue, you have multiple actions you can take on the messages you receive. -> **NOTE**: You can only settle `ReceivedMessage` objects which are received in `ReceiveMode.PeekLock` mode (this is the default). -> `ReceiveMode.ReceiveAndDelete` mode removes the message from the queue on receipt. `PeekedMessage` messages -> returned from `peek()` cannot be settled, as the message lock is not taken like it is in the aforementioned receive methods. Sessionful messages have a similar limitation. +> **NOTE**: You can only settle `ServiceBusReceivedMessage` objects which are received in `ReceiveMode.PeekLock` mode (this is the default). +> `ReceiveMode.ReceiveAndDelete` mode removes the message from the queue on receipt. `ServiceBusReceivedMessage` messages +> returned from `peek_messages()` cannot be settled, as the message lock is not taken like it is in the aforementioned receive methods. Sessionful messages have a similar limitation. If the message has a lock as mentioned above, settlement will fail if the message lock has expired. -If processing would take longer than the lock duration, it must be maintained via `message.renew_lock()` before it expires. +If processing would take longer than the lock duration, it must be maintained via `receiver.renew_message_lock` before it expires. Lock duration is set in Azure on the queue or topic itself. See [AutoLockRenewer](#automatically-renew-message-or-session-locks) for a helper to perform this in the background automatically. @@ -260,7 +260,7 @@ with ServiceBusClient.from_connection_string(connstr) as client: with client.get_queue_receiver(queue_name) as receiver: for msg in receiver: print(str(msg)) - msg.complete() + receiver.complete_message(msg) ``` #### [Abandon][abandon_reference] @@ -278,7 +278,7 @@ with ServiceBusClient.from_connection_string(connstr) as client: with client.get_queue_receiver(queue_name) as receiver: for msg in receiver: print(str(msg)) - msg.abandon() + receiver.abandon_message(receiver) ``` #### [DeadLetter][deadletter_reference] @@ -296,7 +296,7 @@ with ServiceBusClient.from_connection_string(connstr) as client: with client.get_queue_receiver(queue_name) as receiver: for msg in receiver: print(str(msg)) - msg.dead_letter() + receiver.dead_letter_message(msg) ``` #### [Defer][defer_reference] @@ -315,32 +315,53 @@ with ServiceBusClient.from_connection_string(connstr) as client: with client.get_queue_receiver(queue_name) as receiver: for msg in receiver: print(str(msg)) - msg.defer() + receiver.defer_message(msg) ``` ### [Automatically renew Message or Session locks][autolockrenew_reference] -`AutoLockRenewer` is a simple method for ensuring your message or session remains locked even over long periods of time, if calling `renew_lock()` is impractical or undesired. -Internally, it is not much more than shorthand for creating a concurrent watchdog to call `renew_lock()` if the object is nearing expiry. +`AutoLockRenewer` is a simple method for ensuring your message or session remains locked even over long periods of time, if calling `receiver.renew_message_lock`/`receiver.session.renew_lock` is impractical or undesired. +Internally, it is not much more than shorthand for creating a concurrent watchdog to do lock renewal if the object is nearing expiry. It should be used as follows: +* Message lock automatic renewing + ```python from azure.servicebus import ServiceBusClient, AutoLockRenewer import os connstr = os.environ['SERVICE_BUS_CONN_STR'] queue_name = os.environ['SERVICE_BUS_QUEUE_NAME'] + +# Can also be called via "with AutoLockRenewer() as renewer" to automate closing. +renewer = AutoLockRenewer() +with ServiceBusClient.from_connection_string(connstr) as client: + with client.get_queue_receiver(queue_name) as receiver: + for msg in receiver.receive_messages(): + renewer.register(receiver, msg, timeout=60) + # Do your application logic here + receiver.complete_message(msg) +renewer.close() +``` + +* Session lock automatic renewing + +```python +from azure.servicebus import ServiceBusClient, AutoLockRenewer + +import os +connstr = os.environ['SERVICE_BUS_CONN_STR'] +session_queue_name = os.environ['SERVICE_BUS_SESSION_QUEUE_NAME'] session_id = os.environ['SERVICE_BUS_SESSION_ID'] # Can also be called via "with AutoLockRenewer() as renewer" to automate closing. renewer = AutoLockRenewer() with ServiceBusClient.from_connection_string(connstr) as client: - with client.get_queue_receiver(queue_name, session_id=session_id) as receiver: - renewer.register(receiver.session, timeout=300) # Timeout for how long to maintain the lock for, in seconds. + with client.get_queue_receiver(session_queue_name, session_id=session_id) as receiver: + renewer.register(receiver, receiver.session, timeout=300) # Timeout for how long to maintain the lock for, in seconds. for msg in receiver.receive_messages(): - renewer.register(msg, timeout=60) # Do your application logic here - msg.complete() + receiver.complete_message(msg) renewer.close() ``` @@ -364,7 +385,7 @@ link will extend this timeout. - max_wait_time: Provided on creation of a receiver or when calling `receive_messages()` or `get_streaming_message_iter()`, the time after which receiving messages will halt after no traffic. This applies both to the imperative `receive_messages()` function as well as the length a generator-style receive will run for before exiting if there are no messages. Passing None (default) will wait forever, up until the 10 minute threshold if no other action is taken. -> **NOTE:** If processing of a message or session is sufficiently long as to cause timeouts, as an alternative to calling `renew_lock()` manually, one can +> **NOTE:** If processing of a message or session is sufficiently long as to cause timeouts, as an alternative to calling `receiver.renew_message_lock`/`receiver.session.renew_lock` manually, one can > leverage the `AutoLockRenewer` functionality detailed [above](#automatically-renew-message-or-session-locks). ### Common Exceptions @@ -408,7 +429,7 @@ You should be aware of the lock duration of a session and keep renewing the lock This could happen when the receiver used by `AutoLockRenerer` is closed or the lock of the renewable has expired. It is recommended to re-register the renewable message or session by receiving the message or connect to the sessionful entity again. - **AutoLockRenewTimeout:** The time allocated to renew the message or session lock has elapsed. You could re-register the object that wants be auto lock renewed or extend the timeout in advance. -- **MessageError:** Operation on message failed because the message is in a wrong state. It is the root error class of message related errors described above. +- **ServiceBusMessageError:** Operation on message failed because the message is in a wrong state. It is the root error class of message related errors described above. - **ServiceBusError:** All other Service Bus related errors. It is the root error class of all the errors described above. Please view the [exceptions reference docs][exception_reference] for detailed descriptions of our common Exception types. @@ -479,10 +500,10 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio [streaming_receive_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=get_streaming_message_iter#azure.servicebus.ServiceBusReceiver.get_streaming_message_iter [session_receive_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=receive#azure.servicebus.ServiceBusSessionReceiver.receive_messages [session_send_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=session_id#azure.servicebus.ServiceBusMessage.session_id -[complete_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=complete#azure.servicebus.ServiceBusReceivedMessage.complete -[abandon_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=abandon#azure.servicebus.ServiceBusReceivedMessage.abandon -[defer_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=defer#azure.servicebus.ServiceBusReceivedMessage.defer -[deadletter_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=dead_letter#azure.servicebus.ServiceBusReceivedMessage.dead_letter +[complete_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=complete_message#azure.servicebus.ServiceBusReceiver.complete_message +[abandon_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=abandon_message#azure.servicebus.ServiceBusReceiver.abandon_message +[defer_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=defer_message#azure.servicebus.ServiceBusReceiver.defer_message +[deadletter_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html?highlight=dead_letter_message#azure.servicebus.ServiceBusReceiver.dead_letter_message [autolockrenew_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html#azure.servicebus.AutoLockRenewer [exception_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.html#module-azure.servicebus.exceptions [subscription_reference]: https://azuresdkdocs.blob.core.windows.net/$web/python/azure-servicebus/latest/azure.servicebus.aio.html?highlight=subscription#azure.servicebus.aio.ServiceBusClient.get_subscription_receiver diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py index 77f2f3eaf651..18a8cbfc2788 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py @@ -12,12 +12,7 @@ from ._servicebus_sender import ServiceBusSender from ._servicebus_receiver import ServiceBusReceiver from ._servicebus_session import ServiceBusSession -from ._common.message import ( - ServiceBusMessage, - ServiceBusMessageBatch, - ServiceBusPeekedMessage, - ServiceBusReceivedMessage -) +from ._common.message import ServiceBusMessage, ServiceBusMessageBatch, ServiceBusReceivedMessage from ._common.constants import ReceiveMode, SubQueue, NEXT_AVAILABLE_SESSION from ._common.auto_lock_renewer import AutoLockRenewer @@ -26,7 +21,6 @@ __all__ = [ 'ServiceBusMessage', 'ServiceBusMessageBatch', - 'ServiceBusPeekedMessage', 'ServiceBusReceivedMessage', 'NEXT_AVAILABLE_SESSION', 'SubQueue', diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/auto_lock_renewer.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/auto_lock_renewer.py index 379f9cf8f2eb..96055b5c7edb 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/auto_lock_renewer.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/auto_lock_renewer.py @@ -11,6 +11,7 @@ from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING +from .._servicebus_receiver import ServiceBusReceiver from .._servicebus_session import ServiceBusSession from ..exceptions import AutoLockRenewFailed, AutoLockRenewTimeout, ServiceBusError from .utils import renewable_start_time, utc_now @@ -20,9 +21,11 @@ from .message import ServiceBusReceivedMessage LockRenewFailureCallback = Callable[[Union[ServiceBusSession, ServiceBusReceivedMessage], Optional[Exception]], None] + Renewable = Union[ServiceBusSession, ServiceBusReceivedMessage] _log = logging.getLogger(__name__) + class AutoLockRenewer(object): """Auto renew locks for messages and sessions using a background thread pool. @@ -90,11 +93,11 @@ def _renewable(self, renewable): return False return True - def _auto_lock_renew(self, renewable, starttime, timeout, on_lock_renew_failure=None): + def _auto_lock_renew(self, receiver, renewable, starttime, timeout, on_lock_renew_failure=None): # pylint: disable=protected-access _log.debug("Running lock auto-renew thread for %r seconds", timeout) error = None - clean_shutdown = False # Only trigger the on_lock_renew_failure if halting was not expected (shutdown, etc) + clean_shutdown = False # Only trigger the on_lock_renew_failure if halting was not expected (shutdown, etc) try: while self._renewable(renewable): if (utc_now() - starttime) >= datetime.timedelta(seconds=timeout): @@ -102,7 +105,12 @@ def _auto_lock_renew(self, renewable, starttime, timeout, on_lock_renew_failure= raise AutoLockRenewTimeout("Auto-renew period ({} seconds) elapsed.".format(timeout)) if (renewable.locked_until_utc - utc_now()) <= datetime.timedelta(seconds=self._renew_period): _log.debug("%r seconds or less until lock expires - auto renewing.", self._renew_period) - renewable.renew_lock() + try: + # Renewable is a session + renewable.renew_lock() # type: ignore + except AttributeError: + # Renewable is a message + receiver.renew_message_lock(renewable) # type: ignore time.sleep(self._sleep_time) clean_shutdown = not renewable._lock_expired except AutoLockRenewTimeout as e: @@ -119,10 +127,13 @@ def _auto_lock_renew(self, renewable, starttime, timeout, on_lock_renew_failure= if on_lock_renew_failure and not clean_shutdown: on_lock_renew_failure(renewable, error) - def register(self, renewable, timeout=300, on_lock_renew_failure=None): - # type: (Union[ServiceBusReceivedMessage, ServiceBusSession], float, Optional[LockRenewFailureCallback]) -> None + def register(self, receiver, renewable, timeout=300, on_lock_renew_failure=None): + # type: (ServiceBusReceiver, Renewable, float, Optional[LockRenewFailureCallback]) -> None """Register a renewable entity for automatic lock renewal. + :param receiver: The ServiceBusReceiver instance that is associated with the message or the session to + be auto-lock-renewed. + :type receiver: ~azure.servicebus.ServiceBusReceiver :param renewable: A locked entity that needs to be renewed. :type renewable: Union[~azure.servicebus.ServiceBusReceivedMessage, ~azure.servicebus.ServiceBusSession] :param timeout: A time in seconds that the lock should be maintained for. Default value is 300 (5 minutes). @@ -137,7 +148,7 @@ def register(self, renewable, timeout=300, on_lock_renew_failure=None): raise ServiceBusError("The AutoLockRenewer has already been shutdown. Please create a new instance for" " auto lock renewing.") starttime = renewable_start_time(renewable) - self._executor.submit(self._auto_lock_renew, renewable, starttime, timeout, on_lock_renew_failure) + self._executor.submit(self._auto_lock_renew, receiver, renewable, starttime, timeout, on_lock_renew_failure) def close(self, wait=True): """Cease autorenewal by shutting down the thread pool to clean up any remaining lock renewal threads. diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/constants.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/constants.py index 5ef250e81488..ec0bdacc6e7d 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/constants.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/constants.py @@ -41,11 +41,6 @@ REQUEST_RESPONSE_REMOVE_RULE_OPERATION = VENDOR + b":remove-rule" REQUEST_RESPONSE_GET_RULES_OPERATION = VENDOR + b":enumerate-rules" -SETTLEMENT_COMPLETE = "completed" -SETTLEMENT_ABANDON = "abandoned" -SETTLEMENT_DEFER = "defered" -SETTLEMENT_DEADLETTER = "suspended" - CONTAINER_PREFIX = "servicebus.pysdk-" JWT_TOKEN_SCOPE = "https://servicebus.azure.net//.default" USER_AGENT_PREFIX = "azsdk-python-servicebus" @@ -80,6 +75,19 @@ MESSAGE_DEFER = 'defer' MESSAGE_RENEW_LOCK = 'renew' +SETTLEMENT_COMPLETE = "completed" +SETTLEMENT_ABANDON = "abandoned" +SETTLEMENT_DEFER = "defered" +SETTLEMENT_DEADLETTER = "suspended" + +# The following dict maps the term of settlement actions to the ones used in mgmt request defined by the service. +MESSAGE_MGMT_SETTLEMENT_TERM_MAP = { + MESSAGE_COMPLETE: SETTLEMENT_COMPLETE, + MESSAGE_ABANDON: SETTLEMENT_ABANDON, + MESSAGE_DEFER: SETTLEMENT_DEFER, + MESSAGE_DEAD_LETTER: SETTLEMENT_DEADLETTER +} + TOKEN_TYPE_JWT = b"jwt" TOKEN_TYPE_SASTOKEN = b"servicebus.windows.net:sastoken" diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py index 462cf27380b5..aa6c4cb0c2a8 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py @@ -7,10 +7,9 @@ import datetime import uuid -import functools import logging import copy -from typing import Optional, List, Union, Iterable, TYPE_CHECKING, Callable, Any +from typing import Optional, List, Union, Iterable, TYPE_CHECKING, Any import uamqp.errors import uamqp.message @@ -18,10 +17,6 @@ from .constants import ( _BATCH_MESSAGE_OVERHEAD_COST, - SETTLEMENT_ABANDON, - SETTLEMENT_COMPLETE, - SETTLEMENT_DEFER, - SETTLEMENT_DEADLETTER, ReceiveMode, _X_OPT_ENQUEUED_TIME, _X_OPT_SEQUENCE_NUMBER, @@ -32,17 +27,6 @@ _X_OPT_LOCK_TOKEN, _X_OPT_SCHEDULED_ENQUEUE_TIME, _X_OPT_DEAD_LETTER_SOURCE, - MGMT_RESPONSE_MESSAGE_EXPIRATION, - MGMT_REQUEST_DEAD_LETTER_REASON, - MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION, - RECEIVER_LINK_DEAD_LETTER_REASON, - RECEIVER_LINK_DEAD_LETTER_ERROR_DESCRIPTION, - MESSAGE_COMPLETE, - MESSAGE_DEAD_LETTER, - MESSAGE_ABANDON, - MESSAGE_DEFER, - MESSAGE_RENEW_LOCK, - DEADLETTERNAME, PROPERTIES_DEAD_LETTER_REASON, PROPERTIES_DEAD_LETTER_ERROR_DESCRIPTION, ANNOTATION_SYMBOL_PARTITION_KEY, @@ -50,16 +34,10 @@ ANNOTATION_SYMBOL_SCHEDULED_ENQUEUE_TIME, ANNOTATION_SYMBOL_KEY_MAP ) -from ..exceptions import ( - ServiceBusMessageError, - MessageAlreadySettled, - MessageLockExpired, - SessionLockExpired, - MessageSettleFailed, - MessageContentTooLarge -) +from ..exceptions import MessageContentTooLarge from .utils import utc_from_timestamp, utc_now, transform_messages_to_sendable_if_needed if TYPE_CHECKING: + from ..aio._servicebus_receiver_async import ServiceBusReceiver as AsyncServiceBusReceiver from .._servicebus_receiver import ServiceBusReceiver _LOGGER = logging.getLogger(__name__) @@ -580,17 +558,56 @@ def add_message(self, message): self._messages.append(message) -class ServiceBusPeekedMessage(ServiceBusMessage): - """A preview message. +class ServiceBusReceivedMessage(ServiceBusMessage): + """ + A Service Bus Message received from service side. + + :ivar auto_renew_error: Error when AutoLockRenewer is used and it fails to renew the message lock. + :vartype auto_renew_error: ~azure.servicebus.AutoLockRenewTimeout or ~azure.servicebus.AutoLockRenewFailed + + .. admonition:: Example: + + .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py + :start-after: [START receive_complex_message] + :end-before: [END receive_complex_message] + :language: python + :dedent: 4 + :caption: Checking the properties on a received message. - This message is still on the queue, and unlocked. - A peeked message cannot be completed, abandoned, dead-lettered or deferred. - It has no lock token or expiry. """ + def __init__(self, message, receive_mode=ReceiveMode.PeekLock, **kwargs): + # type: (uamqp.message.Message, ReceiveMode, Any) -> None + super(ServiceBusReceivedMessage, self).__init__(None, message=message) # type: ignore + self._settled = (receive_mode == ReceiveMode.ReceiveAndDelete) + self._received_timestamp_utc = utc_now() + self._is_deferred_message = kwargs.get("is_deferred_message", False) + self._is_peeked_message = kwargs.get("is_peeked_message", False) + self.auto_renew_error = None # type: Optional[Exception] + try: + self._receiver = kwargs.pop("receiver") # type: Union[ServiceBusReceiver, AsyncServiceBusReceiver] + except KeyError: + raise TypeError("ServiceBusReceivedMessage requires a receiver to be initialized. " + + "This class should never be initialized by a user; " + + "for outgoing messages, the ServiceBusMessage class should be utilized instead.") + self._expiry = None # type: Optional[datetime.datetime] - def __init__(self, message): - # type: (uamqp.message.Message) -> None - super(ServiceBusPeekedMessage, self).__init__(None, message=message) # type: ignore + @property + def _lock_expired(self): + # type: () -> bool + # pylint: disable=protected-access + """ + Whether the lock on the message has expired. + + :rtype: bool + """ + try: + if self._receiver.session: # type: ignore + raise TypeError("Session messages do not expire. Please use the Session expiry instead.") + except AttributeError: # Is not a session receiver + pass + if self.locked_until_utc and self.locked_until_utc <= utc_now(): + return True + return False def _to_outgoing_message(self): # type: () -> ServiceBusMessage @@ -737,127 +754,6 @@ def sequence_number(self): return self.message.annotations.get(_X_OPT_SEQUENCE_NUMBER) return None - -class ServiceBusReceivedMessageBase(ServiceBusPeekedMessage): - """ - A Service Bus Message received from service side. - - :ivar auto_renew_error: Error when AutoLockRenewer is used and it fails to renew the message lock. - :vartype auto_renew_error: ~azure.servicebus.AutoLockRenewTimeout or ~azure.servicebus.AutoLockRenewFailed - - .. admonition:: Example: - - .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py - :start-after: [START receive_complex_message] - :end-before: [END receive_complex_message] - :language: python - :dedent: 4 - :caption: Checking the properties on a received message. - """ - - def __init__(self, message, receive_mode=ReceiveMode.PeekLock, **kwargs): - # type: (uamqp.message.Message, ReceiveMode, Any) -> None - super(ServiceBusReceivedMessageBase, self).__init__(message=message) - self._settled = (receive_mode == ReceiveMode.ReceiveAndDelete) - self._received_timestamp_utc = utc_now() - self._is_deferred_message = kwargs.get("is_deferred_message", False) - self.auto_renew_error = None # type: Optional[Exception] - try: - self._receiver = kwargs.pop("receiver") # type: Union[ServiceBusReceiver] - except KeyError: - raise TypeError("ReceivedMessage requires a receiver to be initialized. This class should never be" + \ - "initialized by a user; the Message class should be utilized instead.") - self._expiry = None # type: Optional[datetime.datetime] - - def _check_live(self, action): - # pylint: disable=no-member - if not self._receiver or not self._receiver._running: # pylint: disable=protected-access - raise MessageSettleFailed(action, error=ServiceBusMessageError("Orphan message had no open connection.")) - if self._settled: - raise MessageAlreadySettled(action) - try: - if self._lock_expired: - raise MessageLockExpired(error=self.auto_renew_error) - except TypeError: - pass - try: - if self._receiver.session._lock_expired: # pylint: disable=protected-access - raise SessionLockExpired(error=self._receiver.session.auto_renew_error) - except AttributeError: - pass - - def _settle_via_mgmt_link(self, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): - # type: (str, Optional[str], Optional[str]) -> Callable - # pylint: disable=protected-access - - if settle_operation == MESSAGE_COMPLETE: - return functools.partial( - self._receiver._settle_message, - SETTLEMENT_COMPLETE, - [self.lock_token], - ) - if settle_operation == MESSAGE_ABANDON: - return functools.partial( - self._receiver._settle_message, - SETTLEMENT_ABANDON, - [self.lock_token], - ) - if settle_operation == MESSAGE_DEAD_LETTER: - return functools.partial( - self._receiver._settle_message, - SETTLEMENT_DEADLETTER, - [self.lock_token], - dead_letter_details={ - MGMT_REQUEST_DEAD_LETTER_REASON: dead_letter_reason or "", - MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION: dead_letter_error_description or "" - } - ) - if settle_operation == MESSAGE_DEFER: - return functools.partial( - self._receiver._settle_message, - SETTLEMENT_DEFER, - [self.lock_token], - ) - raise ValueError("Unsupported settle operation type: {}".format(settle_operation)) - - def _settle_via_receiver_link(self, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): - # type: (str, Optional[str], Optional[str]) -> Callable - if settle_operation == MESSAGE_COMPLETE: - return functools.partial(self.message.accept) - if settle_operation == MESSAGE_ABANDON: - return functools.partial(self.message.modify, True, False) - if settle_operation == MESSAGE_DEAD_LETTER: - return functools.partial( - self.message.reject, - condition=DEADLETTERNAME, - description=dead_letter_error_description, - info={ - RECEIVER_LINK_DEAD_LETTER_REASON: dead_letter_reason, - RECEIVER_LINK_DEAD_LETTER_ERROR_DESCRIPTION: dead_letter_error_description - } - ) - if settle_operation == MESSAGE_DEFER: - return functools.partial(self.message.modify, True, True) - raise ValueError("Unsupported settle operation type: {}".format(settle_operation)) - - @property - def _lock_expired(self): - # type: () -> bool - # pylint: disable=protected-access - """ - Whether the lock on the message has expired. - - :rtype: bool - """ - try: - if self._receiver.session: # type: ignore - raise TypeError("Session messages do not expire. Please use the Session expiry instead.") - except AttributeError: # Is not a session receiver - pass - if self.locked_until_utc and self.locked_until_utc <= utc_now(): - return True - return False - @property def lock_token(self): # type: () -> Optional[Union[uuid.UUID, str]] @@ -902,214 +798,6 @@ def locked_until_utc(self): return self._expiry -class ServiceBusReceivedMessage(ServiceBusReceivedMessageBase): - def _settle_message( - self, - settle_operation, - dead_letter_reason=None, - dead_letter_error_description=None - ): - # type: (str, Optional[str], Optional[str]) -> None - try: - if not self._is_deferred_message: - try: - self._settle_via_receiver_link(settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description)() - return - except RuntimeError as exception: - _LOGGER.info( - "Message settling: %r has encountered an exception (%r)." - "Trying to settle through management link", - settle_operation, - exception - ) - self._settle_via_mgmt_link(settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description)() - except Exception as exception: # pylint: disable=broad-except - _LOGGER.info( - "Message settling: %r has encountered an exception (%r) through management link", - settle_operation, - exception - ) - raise - - def _settle_message_with_retry( - self, - settle_operation, - dead_letter_reason=None, - dead_letter_error_description=None, - **kwargs - ): - # pylint: disable=unused-argument, protected-access - self._receiver._do_retryable_operation( - self._settle_message, - timeout=None, - settle_operation=settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description - ) - - def complete(self): - # type: () -> None - """Complete the message. - - This removes the message from the queue. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - - - .. admonition:: Example: - - .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py - :start-after: [START receive_sync] - :end-before: [END receive_sync] - :language: python - :dedent: 4 - :caption: Completing a received message to remove it from the queue. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_COMPLETE) - self._settle_message_with_retry(MESSAGE_COMPLETE) - self._settled = True - - def dead_letter(self, reason=None, error_description=None): - # type: (Optional[str], Optional[str]) -> None - """Move the message to the Dead Letter queue. - - The Dead Letter queue is a sub-queue that can be - used to store messages that failed to process correctly, or otherwise require further inspection - or processing. The queue can also be configured to send expired messages to the Dead Letter queue. - - :param str reason: The reason for dead-lettering the message. - :param str error_description: The detailed error description for dead-lettering the message. - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - - .. admonition:: Example: - - .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py - :start-after: [START receive_deadletter_sync] - :end-before: [END receive_deadletter_sync] - :language: python - :dedent: 4 - :caption: Dead letter a message to remove it from the queue by sending it to the dead letter subqueue, - and receiving it from there. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_DEAD_LETTER) - self._settle_message_with_retry(MESSAGE_DEAD_LETTER, - dead_letter_reason=reason, - dead_letter_error_description=error_description) - self._settled = True - - def abandon(self): - # type: () -> None - """Abandon the message. - - This message will be returned to the queue and made available to be received again. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - - - .. admonition:: Example: - - .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py - :start-after: [START abandon_message] - :end-before: [END abandon_message] - :language: python - :dedent: 4 - :caption: Abandoning a received message to return it immediately to the queue. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_ABANDON) - self._settle_message_with_retry(MESSAGE_ABANDON) - self._settled = True - - def defer(self): - # type: () -> None - """Defer the message. - - This message will remain in the queue but must be requested - specifically by its sequence number in order to be received. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - - .. admonition:: Example: - - .. literalinclude:: ../samples/sync_samples/sample_code_servicebus.py - :start-after: [START receive_defer_sync] - :end-before: [END receive_defer_sync] - :language: python - :dedent: 4 - :caption: Deferring a received message sets it aside such that it can only be received - by calling receive_deffered_messages with its sequence number - """ - self._check_live(MESSAGE_DEFER) - self._settle_message_with_retry(MESSAGE_DEFER) - self._settled = True - - def renew_lock(self, **kwargs): - # type: (Any) -> datetime.datetime - # pylint: disable=protected-access,no-member - """Renew the message lock. - - This will maintain the lock on the message to ensure it is not returned to the queue - to be reprocessed. - - In order to complete (or otherwise settle) the message, the lock must be maintained, - and cannot already have expired; an expired lock cannot be renewed. - - Messages received via ReceiveAndDelete mode are not locked, and therefore cannot be renewed. - This operation is only available for non-sessionful messages as well. - - Lock renewal can be performed as a background task by registering the message with an - `azure.servicebus.AutoLockRenewer` instance. - - :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. - :returns: The utc datetime the lock is set to expire at. - :rtype: datetime.datetime - :raises: TypeError if the message is sessionful. - :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. - """ - try: - if self._receiver.session: # type: ignore - raise TypeError("Session messages cannot be renewed. Please renew the Session lock instead.") - except AttributeError: - pass - self._check_live(MESSAGE_RENEW_LOCK) - token = self.lock_token - if not token: - raise ValueError("Unable to renew lock - no lock token found.") - - timeout = kwargs.pop("timeout", None) - if timeout is not None and timeout <= 0: - raise ValueError("The timeout must be greater than 0.") - - expiry = self._receiver._renew_locks(token, timeout=timeout) # type: ignore - self._expiry = utc_from_timestamp(expiry[MGMT_RESPONSE_MESSAGE_EXPIRATION][0]/1000.0) # type: datetime.datetime - - return self._expiry - - class AMQPMessage(object): """ The internal AMQP message that this ServiceBusMessage represents. Is read-only. diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/mgmt_handlers.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/mgmt_handlers.py index 98c068bb17d8..832a08872134 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/mgmt_handlers.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/mgmt_handlers.py @@ -5,8 +5,7 @@ # ------------------------------------------------------------------------- import uamqp - -from .message import ServiceBusPeekedMessage, ServiceBusReceivedMessage +from .message import ServiceBusReceivedMessage from ..exceptions import ServiceBusError, MessageLockExpired from .constants import ReceiveMode @@ -29,12 +28,12 @@ def lock_renew_op(status_code, message, description): status_code, description, message.get_data())) -def peek_op(status_code, message, description): +def peek_op(status_code, message, description, receiver): if status_code == 200: parsed = [] for m in message.get_data()[b'messages']: wrapped = uamqp.Message.decode_from_bytes(bytearray(m[b'message'])) - parsed.append(ServiceBusPeekedMessage(wrapped)) + parsed.append(ServiceBusReceivedMessage(wrapped, is_peeked_message=True, receiver=receiver)) return parsed if status_code in [202, 204]: return [] diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/receiver_mixins.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/receiver_mixins.py index bf56b9e9914a..e13a4950fffe 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/receiver_mixins.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/receiver_mixins.py @@ -4,6 +4,9 @@ # license information. # ------------------------------------------------------------------------- import uuid +import functools +from typing import Optional, Callable + from uamqp import Source from .message import ServiceBusReceivedMessage from .constants import ( @@ -12,11 +15,22 @@ SESSION_LOCKED_UNTIL, DATETIMEOFFSET_EPOCH, MGMT_REQUEST_SESSION_ID, - ReceiveMode + ReceiveMode, + DEADLETTERNAME, + RECEIVER_LINK_DEAD_LETTER_REASON, + RECEIVER_LINK_DEAD_LETTER_ERROR_DESCRIPTION, + MESSAGE_COMPLETE, + MESSAGE_DEAD_LETTER, + MESSAGE_ABANDON, + MESSAGE_DEFER ) from ..exceptions import ( _ServiceBusErrorPolicy, - SessionLockExpired + ServiceBusMessageError, + MessageAlreadySettled, + MessageLockExpired, + SessionLockExpired, + MessageSettleFailed ) from .utils import utc_from_timestamp, utc_now @@ -77,6 +91,52 @@ def _get_source(self): return source return self._entity_uri + def _check_message_alive(self, message, action): + # pylint: disable=no-member, protected-access + if message._is_peeked_message: + raise MessageSettleFailed(action, ServiceBusMessageError("Messages received by peek can not be settled.")) + if not self._running: + raise MessageSettleFailed(action, ServiceBusMessageError("Orphan message had no open connection.")) + if message._settled: + raise MessageAlreadySettled(action) + try: + if message._lock_expired: + raise MessageLockExpired(error=message.auto_renew_error) + except TypeError: + pass + try: + if self.session._lock_expired: + raise SessionLockExpired(error=self.session.auto_renew_error) + except AttributeError: + pass + + def _settle_message_via_receiver_link( + self, + message, + settle_operation, + dead_letter_reason=None, + dead_letter_error_description=None + ): + # type: (ServiceBusReceivedMessage, str, Optional[str], Optional[str]) -> Callable + # pylint: disable=no-self-use + if settle_operation == MESSAGE_COMPLETE: + return functools.partial(message.message.accept) + if settle_operation == MESSAGE_ABANDON: + return functools.partial(message.message.modify, True, False) + if settle_operation == MESSAGE_DEAD_LETTER: + return functools.partial( + message.message.reject, + condition=DEADLETTERNAME, + description=dead_letter_error_description, + info={ + RECEIVER_LINK_DEAD_LETTER_REASON: dead_letter_reason, + RECEIVER_LINK_DEAD_LETTER_ERROR_DESCRIPTION: dead_letter_error_description + } + ) + if settle_operation == MESSAGE_DEFER: + return functools.partial(message.message.modify, True, True) + raise ValueError("Unsupported settle operation type: {}".format(settle_operation)) + def _on_attach(self, source, target, properties, error): # pylint: disable=protected-access, unused-argument if self._session and str(source) == self._entity_uri: diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_receiver.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_receiver.py index f9461524179b..852c5ee82a7e 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_receiver.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_receiver.py @@ -2,9 +2,11 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import datetime import time import logging import functools +import uuid from typing import Any, List, TYPE_CHECKING, Optional, Dict, Iterator, Union import six @@ -15,7 +17,7 @@ from ._base_handler import BaseHandler from ._common.utils import create_authentication -from ._common.message import ServiceBusPeekedMessage, ServiceBusReceivedMessage +from ._common.message import ServiceBusReceivedMessage from ._common.constants import ( REQUEST_RESPONSE_RECEIVE_BY_SEQUENCE_NUMBER, REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION, @@ -27,12 +29,24 @@ MGMT_REQUEST_SEQUENCE_NUMBERS, MGMT_REQUEST_RECEIVER_SETTLE_MODE, MGMT_REQUEST_FROM_SEQUENCE_NUMBER, - MGMT_REQUEST_MAX_MESSAGE_COUNT + MGMT_REQUEST_MAX_MESSAGE_COUNT, + MESSAGE_COMPLETE, + MESSAGE_DEAD_LETTER, + MESSAGE_ABANDON, + MESSAGE_DEFER, + MESSAGE_RENEW_LOCK, + MESSAGE_MGMT_SETTLEMENT_TERM_MAP, + MGMT_REQUEST_DEAD_LETTER_REASON, + MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION, + MGMT_RESPONSE_MESSAGE_EXPIRATION ) from ._common import mgmt_handlers from ._common.receiver_mixins import ReceiverMixin + +from ._common.utils import utc_from_timestamp from ._servicebus_session import ServiceBusSession + if TYPE_CHECKING: from azure.core.credentials import TokenCredential @@ -255,9 +269,65 @@ def _receive(self, max_message_count=None, timeout=None): return [self._build_message(message) for message in batch] - def _settle_message(self, settlement, lock_tokens, dead_letter_details=None): - # type: (bytes, List[str], Optional[Dict[str, Any]]) -> Any - # Message settlement through the mgmt link. + def _settle_message_with_retry( + self, + message, + settle_operation, + dead_letter_reason=None, + dead_letter_error_description=None + ): + if not isinstance(message, ServiceBusReceivedMessage): + raise TypeError("Parameter 'message' must be of type ServiceBusReceivedMessage") + self._check_message_alive(message, settle_operation) + self._do_retryable_operation( + self._settle_message, + timeout=None, + message=message, + settle_operation=settle_operation, + dead_letter_reason=dead_letter_reason, + dead_letter_error_description=dead_letter_error_description + ) + message._settled = True # pylint: disable=protected-access + + def _settle_message(self, message, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): + # type: (ServiceBusReceivedMessage, str, Optional[str], Optional[str]) -> None + # pylint: disable=protected-access + try: + if not message._is_deferred_message: + try: + self._settle_message_via_receiver_link( + message, + settle_operation, + dead_letter_reason=dead_letter_reason, + dead_letter_error_description=dead_letter_error_description + )() + return + except RuntimeError as exception: + _LOGGER.info( + "Message settling: %r has encountered an exception (%r)." + "Trying to settle through management link", + settle_operation, + exception + ) + dead_letter_details = { + MGMT_REQUEST_DEAD_LETTER_REASON: dead_letter_reason or "", + MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION: dead_letter_error_description or "" + } if settle_operation == MESSAGE_DEAD_LETTER else None + self._settle_message_via_mgmt_link( + MESSAGE_MGMT_SETTLEMENT_TERM_MAP[settle_operation], + [message.lock_token], # type: ignore + dead_letter_details=dead_letter_details + ) + except Exception as exception: + _LOGGER.info( + "Message settling: %r has encountered an exception (%r) through management link", + settle_operation, + exception + ) + raise + + 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) @@ -267,7 +337,7 @@ def _settle_message(self, settlement, lock_tokens, dead_letter_details=None): if dead_letter_details: message.update(dead_letter_details) - # We don't do retry here, retry is done in the ReceivedMessage._settle_message + # 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, @@ -320,7 +390,7 @@ def get_streaming_message_iter(self, max_wait_time=None): until the connection is closed. If specified, and no messages arrive for the timeout period, the iterator will stop. :type max_wait_time: float - :rtype: Iterator[ReceivedMessage] + :rtype: Iterator[ServiceBusReceivedMessage] .. admonition:: Example: @@ -494,7 +564,7 @@ def receive_deferred_messages(self, sequence_numbers, **kwargs): return messages def peek_messages(self, max_message_count=1, **kwargs): - # type: (int, Any) -> List[ServiceBusPeekedMessage] + # type: (int, Any) -> List[ServiceBusReceivedMessage] """Browse messages currently pending in the queue. Peeked messages are not removed from queue, nor are they locked. They cannot be completed, @@ -506,7 +576,7 @@ def peek_messages(self, max_message_count=1, **kwargs): :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: List[~azure.servicebus.ServiceBusPeekedMessage] + :rtype: List[~azure.servicebus.ServiceBusReceivedMessage] .. admonition:: Example: @@ -537,10 +607,121 @@ def peek_messages(self, max_message_count=1, **kwargs): } self._populate_message_properties(message) - + handler = functools.partial(mgmt_handlers.peek_op, receiver=self) return self._mgmt_request_response_with_retry( REQUEST_RESPONSE_PEEK_OPERATION, message, - mgmt_handlers.peek_op, + handler, timeout=timeout ) + + def complete_message(self, message): + """Complete the message. + + This removes the message from the queue. + + :param message: The received message to be completed. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + self._settle_message_with_retry(message, MESSAGE_COMPLETE) + + def abandon_message(self, message): + """Abandon the message. + + This message will be returned to the queue and made available to be received again. + + :param message: The received message to be abandoned. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + self._settle_message_with_retry(message, MESSAGE_ABANDON) + + def defer_message(self, message): + """Defers the message. + + This message will remain in the queue but must be requested + specifically by its sequence number in order to be received. + + :param message: The received message to be deferred. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + self._settle_message_with_retry(message, MESSAGE_DEFER) + + def dead_letter_message(self, message, reason=None, error_description=None): + """Move the message to the Dead Letter queue. + + The Dead Letter queue is a sub-queue that can be + used to store messages that failed to process correctly, or otherwise require further inspection + or processing. The queue can also be configured to send expired messages to the Dead Letter queue. + + :param message: The received message to be dead-lettered. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :param Optional[str] reason: The reason for dead-lettering the message. + :param Optional[str] error_description: The detailed error description for dead-lettering the message. + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + self._settle_message_with_retry( + message, + MESSAGE_DEAD_LETTER, + dead_letter_reason=reason, + dead_letter_error_description=error_description + ) + + def renew_message_lock(self, message, **kwargs): + # type: (ServiceBusReceivedMessage, Any) -> datetime.datetime + # pylint: disable=protected-access,no-member + """Renew the message lock. + + This will maintain the lock on the message to ensure it is not returned to the queue + to be reprocessed. + + In order to complete (or otherwise settle) the message, the lock must be maintained, + and cannot already have expired; an expired lock cannot be renewed. + + Messages received via ReceiveAndDelete mode are not locked, and therefore cannot be renewed. + This operation is only available for non-sessionful messages as well. + + :param message: The message to renew the lock for. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :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. + :returns: The utc datetime the lock is set to expire at. + :rtype: datetime.datetime + :raises: TypeError if the message is sessionful. + :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. + """ + # type: ignore + try: + if self.session: + raise TypeError("Session messages cannot be renewed. Please renew the session lock instead.") + except AttributeError: + pass + + self._check_message_alive(message, MESSAGE_RENEW_LOCK) + token = message.lock_token + if not token: + raise ValueError("Unable to renew lock - no lock token found.") + + timeout = kwargs.pop("timeout", None) + if timeout is not None and timeout <= 0: + raise ValueError("The timeout must be greater than 0.") + + expiry = self._renew_locks(token, timeout=timeout) # type: ignore + message._expiry = utc_from_timestamp(expiry[MGMT_RESPONSE_MESSAGE_EXPIRATION][0]/1000.0) + + return message._expiry # type: ignore diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/__init__.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/__init__.py index 12e3ee0bc2bf..79ee3ab71339 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/__init__.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/__init__.py @@ -3,7 +3,6 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # ------------------------------------------------------------------------- -from ._async_message import ServiceBusReceivedMessage from ._servicebus_sender_async import ServiceBusSender from ._servicebus_receiver_async import ServiceBusReceiver from ._servicebus_session_async import ServiceBusSession @@ -11,7 +10,6 @@ from ._async_auto_lock_renewer import AutoLockRenewer __all__ = [ - 'ServiceBusReceivedMessage', 'ServiceBusClient', 'ServiceBusSender', 'ServiceBusReceiver', diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_auto_lock_renewer.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_auto_lock_renewer.py index 8045241a0e96..64f913433a98 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_auto_lock_renewer.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_auto_lock_renewer.py @@ -9,14 +9,16 @@ import datetime from typing import Optional, Iterable, Any, Union, Callable, Awaitable, List -from ._async_message import ServiceBusReceivedMessage +from .._common.message import ServiceBusReceivedMessage from ._servicebus_session_async import ServiceBusSession +from ._servicebus_receiver_async import ServiceBusReceiver from .._common.utils import renewable_start_time, utc_now from ._async_utils import get_running_loop from ..exceptions import AutoLockRenewTimeout, AutoLockRenewFailed, ServiceBusError AsyncLockRenewFailureCallback = Callable[[Union[ServiceBusSession, ServiceBusReceivedMessage], Optional[Exception]], Awaitable[None]] +Renewable = Union[ServiceBusSession, ServiceBusReceivedMessage] _log = logging.getLogger(__name__) @@ -77,14 +79,17 @@ def _renewable(self, renewable: Union[ServiceBusReceivedMessage, ServiceBusSessi return False except AttributeError: # If for whatever reason the renewable isn't hooked up to a receiver raise ServiceBusError("Cannot renew an entity without an associated receiver. " - "ReceivedMessage and active ServiceBusReceiver.Session objects are expected.") + "ServiceBusReceivedMessage and active ServiceBusReceiver.Session objects are expected.") return True - async def _auto_lock_renew(self, - renewable: Union[ServiceBusReceivedMessage, ServiceBusSession], - starttime: datetime.datetime, - timeout: float, - on_lock_renew_failure: Optional[AsyncLockRenewFailureCallback] = None) -> None: + async def _auto_lock_renew( + self, + receiver: ServiceBusReceiver, + renewable: Renewable, + starttime: datetime.datetime, + timeout: float, + on_lock_renew_failure: Optional[AsyncLockRenewFailureCallback] = None + ) -> None: # pylint: disable=protected-access _log.debug("Running async lock auto-renew for %r seconds", timeout) error = None # type: Optional[Exception] @@ -96,7 +101,12 @@ async def _auto_lock_renew(self, raise AutoLockRenewTimeout("Auto-renew period ({} seconds) elapsed.".format(timeout)) if (renewable.locked_until_utc - utc_now()) <= datetime.timedelta(seconds=self._renew_period): _log.debug("%r seconds or less until lock expires - auto renewing.", self._renew_period) - await renewable.renew_lock() + try: + # Renewable is a session + await renewable.renew_lock() # type: ignore + except AttributeError: + # Renewable is a message + await receiver.renew_message_lock(renewable) # type: ignore await asyncio.sleep(self._sleep_time) clean_shutdown = not renewable._lock_expired except AutoLockRenewTimeout as e: @@ -115,12 +125,16 @@ async def _auto_lock_renew(self, def register( self, + receiver: ServiceBusReceiver, renewable: Union[ServiceBusReceivedMessage, ServiceBusSession], timeout: float = 300, on_lock_renew_failure: Optional[AsyncLockRenewFailureCallback] = None ) -> None: """Register a renewable entity for automatic lock renewal. + :param receiver: The ServiceBusReceiver instance that is associated with the message or the session to + be auto-lock-renewed. + :type receiver: ~azure.servicebus.aio.ServiceBusReceiver :param renewable: A locked entity that needs to be renewed. :type renewable: Union[~azure.servicebus.aio.ServiceBusReceivedMessage,~azure.servicebus.aio.ServiceBusSession] :param float timeout: A time in seconds that the lock should be maintained for. @@ -135,7 +149,7 @@ def register( " auto lock renewing.") starttime = renewable_start_time(renewable) renew_future = asyncio.ensure_future( - self._auto_lock_renew(renewable, starttime, timeout, on_lock_renew_failure), + self._auto_lock_renew(receiver, renewable, starttime, timeout, on_lock_renew_failure), loop=self._loop) self._futures.append(renew_future) diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py deleted file mode 100644 index b6e138923d17..000000000000 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py +++ /dev/null @@ -1,190 +0,0 @@ -# ------------------------------------------------------------------------ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# ------------------------------------------------------------------------- -import logging -import datetime -from typing import Any, Optional - -from .._common import message as sync_message -from .._common.constants import ( - MGMT_RESPONSE_MESSAGE_EXPIRATION, - MESSAGE_COMPLETE, - MESSAGE_DEAD_LETTER, - MESSAGE_ABANDON, - MESSAGE_DEFER, - MESSAGE_RENEW_LOCK -) -from .._common.utils import utc_from_timestamp -from ._async_utils import get_running_loop - -_LOGGER = logging.getLogger(__name__) - - -class ServiceBusReceivedMessage(sync_message.ServiceBusReceivedMessageBase): - """A Service Bus Message received from service side. - - """ - - async def _settle_message_with_retry( - self, - settle_operation, - dead_letter_reason=None, - dead_letter_error_description=None, - **kwargs - ): - # pylint: disable=unused-argument, protected-access - await self._receiver._do_retryable_operation( - self._settle_message, - timeout=None, - settle_operation=settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description - ) - - async def _settle_message( # type: ignore - self, - settle_operation, - dead_letter_reason=None, - dead_letter_error_description=None, - ): - try: - if not self._is_deferred_message: - try: - await get_running_loop().run_in_executor( - None, - self._settle_via_receiver_link( - settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description - ) - ) - return - except RuntimeError as exception: - _LOGGER.info( - "Message settling: %r has encountered an exception (%r)." - "Trying to settle through management link", - settle_operation, - exception - ) - await self._settle_via_mgmt_link(settle_operation, - dead_letter_reason=dead_letter_reason, - dead_letter_error_description=dead_letter_error_description)() - except Exception as exception: # pylint: disable=broad-except - _LOGGER.info( - "Message settling: %r has encountered an exception (%r) through management link", - settle_operation, - exception - ) - raise - - async def complete(self) -> None: # type: ignore - """Complete the message. - - This removes the message from the queue. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_COMPLETE) - await self._settle_message_with_retry(MESSAGE_COMPLETE) - self._settled = True - - async def dead_letter( # type: ignore - self, reason: Optional[str] = None, - error_description: Optional[str] = None - ) -> None: # pylint: disable=unused-argument - """Move the message to the Dead Letter queue. - - The Dead Letter queue is a sub-queue that can be - used to store messages that failed to process correctly, or otherwise require further inspection - or processing. The queue can also be configured to send expired messages to the Dead Letter queue. - - :param Optional[str] reason: The reason for dead-lettering the message. - :param Optional[str] error_description: The detailed error description for dead-lettering the message. - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_DEAD_LETTER) - await self._settle_message_with_retry(MESSAGE_DEAD_LETTER, - dead_letter_reason=reason, - dead_letter_error_description=error_description) - self._settled = True - - async def abandon(self) -> None: # type: ignore - """Abandon the message. - - This message will be returned to the queue and made available to be received again. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_ABANDON) - await self._settle_message_with_retry(MESSAGE_ABANDON) - self._settled = True - - async def defer(self) -> None: # type: ignore - """Defers the message. - - This message will remain in the queue but must be requested - specifically by its sequence number in order to be received. - - :rtype: None - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. - """ - # pylint: disable=protected-access - self._check_live(MESSAGE_DEFER) - await self._settle_message_with_retry(MESSAGE_DEFER) - self._settled = True - - async def renew_lock(self, **kwargs: Any) -> datetime.datetime: - # pylint: disable=protected-access - """Renew the message lock. - - This will maintain the lock on the message to ensure - it is not returned to the queue to be reprocessed. In order to complete (or otherwise settle) - the message, the lock must be maintained. Messages received via ReceiveAndDelete mode are not - locked, and therefore cannot be renewed. This operation can also be performed as an asynchronous - background task by registering the message with an `azure.servicebus.aio.AutoLockRenewer` instance. - This operation is only available for non-sessionful messages. - - :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. - :returns: The utc datetime the lock is set to expire at. - :rtype: datetime.datetime - :raises: TypeError if the message is sessionful. - :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. - :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. - """ - try: - if self._receiver.session: # type: ignore - raise TypeError("Session messages cannot be renewed. Please renew the Session lock instead.") - except AttributeError: - pass - self._check_live(MESSAGE_RENEW_LOCK) - token = self.lock_token - if not token: - raise ValueError("Unable to renew lock - no lock token found.") - - timeout = kwargs.pop("timeout", None) - if timeout is not None and timeout <= 0: - raise ValueError("The timeout must be greater than 0.") - - expiry = await self._receiver._renew_locks(token, timeout=timeout) # type: ignore - self._expiry = utc_from_timestamp(expiry[MGMT_RESPONSE_MESSAGE_EXPIRATION][0]/1000.0) # type: datetime.datetime - - return self._expiry diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_client_async.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_client_async.py index 09e3b59ff051..1c9635fa3e2e 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_client_async.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_client_async.py @@ -2,7 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from typing import Any, List, TYPE_CHECKING, Optional +from typing import Any, List, TYPE_CHECKING import logging import uamqp diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_receiver_async.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_receiver_async.py index a47f7bf970bc..6f9d11d78a81 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_receiver_async.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_receiver_async.py @@ -2,11 +2,12 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import datetime import asyncio import collections import functools import logging -from typing import Any, TYPE_CHECKING, List, Optional, AsyncIterator, Union +from typing import Any, TYPE_CHECKING, List, Optional, AsyncIterator, Union, Callable import six @@ -15,8 +16,7 @@ from ._servicebus_session_async import ServiceBusSession from ._base_handler_async import BaseHandler -from .._common.message import ServiceBusPeekedMessage -from ._async_message import ServiceBusReceivedMessage +from .._common.message import ServiceBusReceivedMessage from .._common.receiver_mixins import ReceiverMixin from .._common.constants import ( REQUEST_RESPONSE_UPDATE_DISPOSTION_OPERATION, @@ -29,10 +29,20 @@ MGMT_REQUEST_SEQUENCE_NUMBERS, MGMT_REQUEST_RECEIVER_SETTLE_MODE, MGMT_REQUEST_FROM_SEQUENCE_NUMBER, - MGMT_REQUEST_MAX_MESSAGE_COUNT + MGMT_REQUEST_MAX_MESSAGE_COUNT, + MESSAGE_COMPLETE, + MESSAGE_DEAD_LETTER, + MESSAGE_ABANDON, + MESSAGE_DEFER, + MESSAGE_RENEW_LOCK, + MESSAGE_MGMT_SETTLEMENT_TERM_MAP, + MGMT_REQUEST_DEAD_LETTER_REASON, + MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION, + MGMT_RESPONSE_MESSAGE_EXPIRATION ) from .._common import mgmt_handlers -from ._async_utils import create_authentication +from .._common.utils import utc_from_timestamp +from ._async_utils import create_authentication, get_running_loop if TYPE_CHECKING: from azure.core.credentials import TokenCredential @@ -173,7 +183,7 @@ async def _iter_next(self): if not self._message_iter: self._message_iter = self._handler.receive_messages_iter_async() uamqp_message = await self._message_iter.__anext__() - message = self._build_message(uamqp_message, ServiceBusReceivedMessage) + message = self._build_message(uamqp_message) return message def _create_handler(self, auth): @@ -253,13 +263,78 @@ async def _receive(self, max_message_count=None, timeout=None): while not received_messages_queue.empty() and len(batch) < max_message_count: batch.append(received_messages_queue.get()) received_messages_queue.task_done() + return [self._build_message(message) for message in batch] - return [self._build_message(message, ServiceBusReceivedMessage) for message in batch] + async def _settle_message_with_retry( + self, + message, + settle_operation, + dead_letter_reason=None, + dead_letter_error_description=None, + ): + if not isinstance(message, ServiceBusReceivedMessage): + raise TypeError("Parameter 'message' must be of type ServiceBusReceivedMessage") + self._check_message_alive(message, settle_operation) + await self._do_retryable_operation( + self._settle_message, + timeout=None, + message=message, + settle_operation=settle_operation, + dead_letter_reason=dead_letter_reason, + dead_letter_error_description=dead_letter_error_description + ) + message._settled = True # pylint: disable=protected-access - async def _settle_message(self, settlement, lock_tokens, dead_letter_details=None): + async def _settle_message( # type: ignore + self, + message: ServiceBusReceivedMessage, + settle_operation: str, + dead_letter_reason: Optional[str] = None, + dead_letter_error_description: Optional[str] = None + ): + # pylint: disable=protected-access + try: + if not message._is_deferred_message: + try: + await get_running_loop().run_in_executor( + None, + self._settle_message_via_receiver_link( + message, + settle_operation, + dead_letter_reason=dead_letter_reason, + dead_letter_error_description=dead_letter_error_description + ) + ) + return + except RuntimeError as exception: + _LOGGER.info( + "Message settling: %r has encountered an exception (%r)." + "Trying to settle through management link", + settle_operation, + exception + ) + dead_letter_details = { + MGMT_REQUEST_DEAD_LETTER_REASON: dead_letter_reason or "", + MGMT_REQUEST_DEAD_LETTER_ERROR_DESCRIPTION: dead_letter_error_description or "" + } if settle_operation == MESSAGE_DEAD_LETTER else None + await self._settle_message_via_mgmt_link( + MESSAGE_MGMT_SETTLEMENT_TERM_MAP[settle_operation], + [message.lock_token], + dead_letter_details=dead_letter_details + ) + except Exception as exception: + _LOGGER.info( + "Message settling: %r has encountered an exception (%r) through management link", + settle_operation, + exception + ) + raise + + 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)} + MGMT_REQUEST_LOCK_TOKENS: types.AMQPArray(lock_tokens) + } self._populate_message_properties(message) if dead_letter_details: @@ -316,7 +391,7 @@ def get_streaming_message_iter( until the connection is closed. If specified, and no messages arrive for the timeout period, the iterator will stop. - :rtype AsyncIterator[ReceivedMessage] + :rtype AsyncIterator[ServiceBusReceivedMessage] .. admonition:: Example: @@ -495,7 +570,7 @@ async def receive_deferred_messages( ) return messages - async def peek_messages(self, max_message_count: int = 1, **kwargs: Any) -> List[ServiceBusPeekedMessage]: + async def peek_messages(self, max_message_count: int = 1, **kwargs: Any) -> List[ServiceBusReceivedMessage]: """Browse messages currently pending in the queue. Peeked messages are not removed from queue, nor are they locked. They cannot be completed, @@ -506,7 +581,7 @@ async def peek_messages(self, max_message_count: int = 1, **kwargs: Any) -> List :keyword int sequence_number: A message sequence number from which to start browsing messages. :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: list[~azure.servicebus.ServiceBusPeekedMessage] + :rtype: list[~azure.servicebus.ServiceBusReceivedMessage] .. admonition:: Example: @@ -537,10 +612,121 @@ async def peek_messages(self, max_message_count: int = 1, **kwargs: Any) -> List } self._populate_message_properties(message) - + handler = functools.partial(mgmt_handlers.peek_op, receiver=self) return await self._mgmt_request_response_with_retry( REQUEST_RESPONSE_PEEK_OPERATION, message, - mgmt_handlers.peek_op, + handler, timeout=timeout ) + + async def complete_message(self, message): + """Complete the message. + + This removes the message from the queue. + + :param message: The received message to be completed. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + await self._settle_message_with_retry(message, MESSAGE_COMPLETE) + + async def abandon_message(self, message): + """Abandon the message. + + This message will be returned to the queue and made available to be received again. + + :param message: The received message to be abandoned. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + await self._settle_message_with_retry(message, MESSAGE_ABANDON) + + async def defer_message(self, message): + """Defers the message. + + This message will remain in the queue but must be requested + specifically by its sequence number in order to be received. + + :param message: The received message to be deferred. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + await self._settle_message_with_retry(message, MESSAGE_DEFER) + + async def dead_letter_message(self, message, reason=None, error_description=None): + """Move the message to the Dead Letter queue. + + The Dead Letter queue is a sub-queue that can be + used to store messages that failed to process correctly, or otherwise require further inspection + or processing. The queue can also be configured to send expired messages to the Dead Letter queue. + + :param message: The received message to be dead-lettered. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :param Optional[str] reason: The reason for dead-lettering the message. + :param Optional[str] error_description: The detailed error description for dead-lettering the message. + :rtype: None + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. + """ + await self._settle_message_with_retry( + message, + MESSAGE_DEAD_LETTER, + dead_letter_reason=reason, + dead_letter_error_description=error_description + ) + + async def renew_message_lock(self, message, **kwargs): + # type: (ServiceBusReceivedMessage, Any) -> datetime.datetime + # pylint: disable=protected-access,no-member + """Renew the message lock. + + This will maintain the lock on the message to ensure it is not returned to the queue + to be reprocessed. + + In order to complete (or otherwise settle) the message, the lock must be maintained, + and cannot already have expired; an expired lock cannot be renewed. + + Messages received via ReceiveAndDelete mode are not locked, and therefore cannot be renewed. + This operation is only available for non-sessionful messages as well. + + :param message: The message to renew the lock for. + :type message: ~azure.servicebus.ServiceBusReceivedMessage + :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. + :returns: The utc datetime the lock is set to expire at. + :rtype: datetime.datetime + :raises: TypeError if the message is sessionful. + :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. + """ + try: + if self.session: + raise TypeError("Session messages cannot be renewed. Please renew the session lock instead.") + except AttributeError: + pass + + self._check_message_alive(message, MESSAGE_RENEW_LOCK) + token = message.lock_token + if not token: + raise ValueError("Unable to renew lock - no lock token found.") + + timeout = kwargs.pop("timeout", None) + if timeout is not None and timeout <= 0: + raise ValueError("The timeout must be greater than 0.") + + expiry = await self._renew_locks(token, timeout=timeout) # type: ignore + message._expiry = utc_from_timestamp(expiry[MGMT_RESPONSE_MESSAGE_EXPIRATION][0]/1000.0) # type: ignore + + return message._expiry # type: ignore diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/exceptions.py b/sdk/servicebus/azure-servicebus/azure/servicebus/exceptions.py index ba07eb19ec11..eedf5d324afc 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/exceptions.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/exceptions.py @@ -106,20 +106,20 @@ def _handle_amqp_connection_error(logger, exception, handler): # In other link detach and connection case, should retry logger.info("Handler detached due to exception: (%r).", exception) if exception.condition == constants.ErrorCodes.UnauthorizedAccess: - error = ServiceBusAuthorizationError(str(exception), exception) + error = ServiceBusAuthorizationError(str(exception), error=exception) elif exception.condition == constants.ErrorCodes.NotAllowed and 'requires sessions' in str(exception): message = str(exception) + '\n\nsession_id must be set when getting a receiver for sessionful entity.' - error = ServiceBusConnectionError(message, exception) + error = ServiceBusConnectionError(message, error=exception) else: - error = ServiceBusConnectionError(str(exception), exception) + error = ServiceBusConnectionError(str(exception), error=exception) elif isinstance(exception, errors.MessageHandlerError): logger.info("Handler error: (%r).", exception) - error = ServiceBusConnectionError(str(exception), exception) + error = ServiceBusConnectionError(str(exception), error=exception) else: # handling general uamqp.errors.AMQPConnectionError logger.info("Failed to open handler: (%r).", exception) message = "Failed to open handler: {}.".format(exception) - error = ServiceBusConnectionError(message, exception) + error = ServiceBusConnectionError(message, error=exception) error_need_raise, error_need_close_handler = True, False return error, error_need_close_handler, error_need_raise @@ -141,7 +141,7 @@ def _handle_amqp_message_error(logger, exception, **kwargs): exception.condition == constants.ErrorCodes.LinkMessageSizeExceeded): # This one doesn't need retry, should raise the error logger.info("Message content is too large (%r).", exception) - error = MessageContentTooLarge("Message content is too large.", exception) + error = MessageContentTooLarge("Message content is too large.", error=exception) error_need_close_handler = False error_need_raise = True else: @@ -168,13 +168,13 @@ def _create_servicebus_exception(logger, exception, handler, **kwargs): # pylin _handle_amqp_message_error(logger, exception, **kwargs) elif isinstance(exception, errors.AuthenticationException): logger.info("Authentication failed due to exception: (%r).", exception) - error = ServiceBusAuthenticationError(str(exception), exception) + error = ServiceBusAuthenticationError(str(exception), error=exception) else: logger.info("Unexpected error occurred (%r). Shutting down.", exception) if kwargs.get("settle_operation"): - error = MessageSettleFailed(kwargs.get("settle_operation"), exception) + error = MessageSettleFailed(kwargs.get("settle_operation"), error=exception) elif not isinstance(exception, ServiceBusError): - error = ServiceBusError("Handler failed: {}.".format(exception), exception) + error = ServiceBusError("Handler failed: {}.".format(exception), error=exception) else: error = exception @@ -280,7 +280,6 @@ class MessageSettleFailed(ServiceBusMessageError): :type error: Exception """ - def __init__(self, action, error): # type: (str, Exception) -> None message = "Failed to {} message. Error: {}".format(action, error) diff --git a/sdk/servicebus/azure-servicebus/migration_guide.md b/sdk/servicebus/azure-servicebus/migration_guide.md index 2db7d24c55b1..cb2884fd8316 100644 --- a/sdk/servicebus/azure-servicebus/migration_guide.md +++ b/sdk/servicebus/azure-servicebus/migration_guide.md @@ -64,7 +64,7 @@ semantics with the sender or receiver lifetime. | In v0.50 | Equivalent in v7 | Sample | |---|---|---| | `queue_client.send(message, session='foo') and queue_client.get_sender(session='foo').send(message)`| `sb_client.get_queue_sender().send_messages(Message('body', session_id='foo'))`| [Send a message to a session](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py) | -| `AutoLockRenew().register(queue_client.get_receiver(session='foo'))`| `AutoLockRenewer().register(sb_client.get_queue_receiver(session_id='foo').session)`| [Access a session and ensure its lock is auto-renewed](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py) | +| `AutoLockRenew().register(queue_client.get_receiver(session='foo'))`| `receiver=sb_client.get_queue_receiver(session_id='foo')`
`AutoLockRenewer().register(receiver, receiver.session)`| [Access a session and ensure its lock is auto-renewed](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py) | | `receiver.get_session_state()` | `receiver.session.get_state()` | [Perform session specific operations on a receiver](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py) ### Working with UTC time @@ -129,11 +129,11 @@ with ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR, receive_mo batch = receiver.receive_messages(max_message_count=10, max_wait_time=5) for message in batch: print("Message: {}".format(message)) - message.complete() + receiver.complete_message(message) for message in receiver: print("Message: {}".format(message)) - message.complete() + receiver.complete_message(message) ``` diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/auto_lock_renew_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/auto_lock_renew_async.py index 2457cff8ad09..6a4746e71e54 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/auto_lock_renew_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/auto_lock_renew_async.py @@ -42,13 +42,13 @@ async def renew_lock_on_message_received_from_non_sessionful_entity(): for msg in received_msgs: # automatically renew the lock on each message for 100 seconds - renewer.register(msg, timeout=100) + renewer.register(receiver, msg, timeout=100) print('Register messages into AutoLockRenewer done.') await asyncio.sleep(100) # message handling for long period (E.g. application logic) for msg in received_msgs: - await msg.complete() + await receiver.complete_message(msg) print('Complete messages.') await renewer.close() @@ -72,14 +72,14 @@ async def renew_lock_on_session_of_the_sessionful_entity(): prefetch_count=10 ) as receiver: # automatically renew the lock on the session for 100 seconds - renewer.register(receiver.session, timeout=100) + renewer.register(receiver, receiver.session, timeout=100) print('Register session into AutoLockRenewer.') received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5) await asyncio.sleep(100) # message handling for long period (E.g. application logic) for msg in received_msgs: - await msg.complete() + await receiver.complete_message(msg) print('Complete messages.') @@ -108,7 +108,7 @@ async def on_lock_renew_failure_callback(renewable, error): for msg in received_msgs: # automatically renew the lock on each message for 120 seconds - renewer.register(msg, timeout=90, on_lock_renew_failure=on_lock_renew_failure_callback) + renewer.register(receiver, msg, timeout=90, on_lock_renew_failure=on_lock_renew_failure_callback) print('Register messages into AutoLockRenewer done.') # Cause the messages and autorenewal to time out. @@ -117,7 +117,7 @@ async def on_lock_renew_failure_callback(renewable, error): try: for msg in received_msgs: - await msg.complete() + await receiver.complete_message(msg) except MessageLockExpired as e: print('Messages cannot be settled if they have timed out. (This is expected)') diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deadlettered_messages_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deadlettered_messages_async.py index ac6d714088fb..ebfbf00a09aa 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deadlettered_messages_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deadlettered_messages_async.py @@ -36,7 +36,7 @@ async def main(): received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - await msg.dead_letter() + await receiver.dead_letter_message(msg) print('receiving deadlettered messages') dlq_receiver = servicebus_client.get_queue_receiver(queue_name=QUEUE_NAME, @@ -46,7 +46,7 @@ async def main(): received_msgs = await dlq_receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - await msg.complete() + await dlq_receiver.complete_message(msg) loop = asyncio.get_event_loop() loop.run_until_complete(main()) diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deferred_message_queue_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deferred_message_queue_async.py index 1704611cd868..4e9392b0cc0c 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deferred_message_queue_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_deferred_message_queue_async.py @@ -37,7 +37,7 @@ async def main(): for msg in received_msgs: print("Deferring msg: {}".format(str(msg))) deferred_sequenced_numbers.append(msg.sequence_number) - await msg.defer() + await receiver.defer_message(msg) if deferred_sequenced_numbers: received_deferred_msg = await receiver.receive_deferred_messages( @@ -46,7 +46,7 @@ async def main(): for msg in received_deferred_msg: print("Completing deferred msg: {}".format(str(msg))) - await msg.complete() + await receiver.complete_message(msg) else: print("No messages received.") diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_iterator_queue_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_iterator_queue_async.py index 6794684bd1ae..6ad50bd8b28c 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_iterator_queue_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_iterator_queue_async.py @@ -27,7 +27,7 @@ async def main(): async with receiver: async for msg in receiver: print(str(msg)) - await msg.complete() + await receiver.complete_message(msg) print("Receive is done.") loop = asyncio.get_event_loop() diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_queue_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_queue_async.py index a510160a713e..b2a26b0f43bd 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_queue_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_queue_async.py @@ -28,7 +28,7 @@ async def main(): received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - await msg.complete() + await receiver.complete_message(msg) loop = asyncio.get_event_loop() loop.run_until_complete(main()) diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_subscription_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_subscription_async.py index fff5f8dc0cd4..840e75269066 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/receive_subscription_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/receive_subscription_async.py @@ -32,7 +32,7 @@ async def main(): received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - await msg.complete() + await receiver.complete_message(msg) loop = asyncio.get_event_loop() loop.run_until_complete(main()) diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/sample_code_servicebus_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/sample_code_servicebus_async.py index 63af74ae9911..32a5fe94f736 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/sample_code_servicebus_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/sample_code_servicebus_async.py @@ -214,14 +214,14 @@ async def example_send_and_receive_async(): messages = await servicebus_receiver.receive_messages(max_wait_time=5) for message in messages: print(str(message)) - await message.complete() + await servicebus_receiver.complete_message(message) # [END receive_async] # [START receive_forever_async] async with servicebus_receiver: async for message in servicebus_receiver.get_streaming_message_iter(): print(str(message)) - await message.complete() + await servicebus_receiver.complete_message(message) # [END receive_forever_async] # [START auto_lock_renew_message_async] @@ -232,7 +232,7 @@ async def example_send_and_receive_async(): async for message in servicebus_receiver: lock_renewal.register(message, timeout=60) await process_message(message) - await message.complete() + await servicebus_receiver.complete_message(message) # [END auto_lock_renew_message_async] @@ -248,14 +248,14 @@ async def example_receive_deferred_async(): for message in messages: deferred_sequenced_numbers.append(message.sequence_number) print(str(message)) - await message.defer() + await servicebus_receiver.defer_message(message) received_deferred_msg = await servicebus_receiver.receive_deferred_messages( sequence_numbers=deferred_sequenced_numbers ) for msg in received_deferred_msg: - await msg.complete() + await servicebus_receiver.complete_message(message) # [END receive_defer_async] @@ -298,7 +298,7 @@ async def example_session_ops_async(): lock_renewal.register(session, timeout=120) async for message in receiver: await process_message(message) - await message.complete() + await receiver.complete_message(message) # [END auto_lock_renew_session_async] break diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/session_pool_receive_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/session_pool_receive_async.py index 41cd0cb2286c..8fe48a46ca8b 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/session_pool_receive_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/session_pool_receive_async.py @@ -23,7 +23,7 @@ async def message_processing(servicebus_client, queue_name): try: async with servicebus_client.get_queue_receiver(queue_name, max_wait_time=1, session_id=NEXT_AVAILABLE_SESSION) as receiver: renewer = AutoLockRenewer() - renewer.register(receiver.session) + renewer.register(receiver, receiver.session) await receiver.session.set_state("OPEN") async for message in receiver: print("Message: {}".format(message)) @@ -34,7 +34,7 @@ async def message_processing(servicebus_client, queue_name): print("Locked until: {}".format(message.locked_until_utc)) print("Lock Token: {}".format(message.lock_token)) print("Enqueued time: {}".format(message.enqueued_time_utc)) - await message.complete() + await receiver.complete_message(message) if str(message) == 'shutdown': await receiver.session.set_state("CLOSED") break diff --git a/sdk/servicebus/azure-servicebus/samples/async_samples/session_send_receive_async.py b/sdk/servicebus/azure-servicebus/samples/async_samples/session_send_receive_async.py index 4661d3436a5a..7efea362769b 100644 --- a/sdk/servicebus/azure-servicebus/samples/async_samples/session_send_receive_async.py +++ b/sdk/servicebus/azure-servicebus/samples/async_samples/session_send_receive_async.py @@ -50,7 +50,7 @@ async def receive_batch_messages(receiver): received_msgs = await receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - await msg.complete() + await receiver.complete_message(msg) await session.renew_lock() await session.set_state("END") print("Session state:", await session.get_state()) diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py index 5ffefd13a18b..cd66619ca3ef 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/auto_lock_renew.py @@ -41,13 +41,13 @@ def renew_lock_on_message_received_from_non_sessionful_entity(): for msg in received_msgs: # automatically renew the lock on each message for 100 seconds - renewer.register(msg, timeout=100) + renewer.register(receiver, msg, timeout=100) print('Register messages into AutoLockRenewer done.') time.sleep(100) # message handling for long period (E.g. application logic) for msg in received_msgs: - msg.complete() # Settling the message deregisters it from the AutoLockRenewer + receiver.complete_message(msg) # Settling the message deregisters it from the AutoLockRenewer print('Complete messages.') renewer.close() @@ -72,14 +72,14 @@ def renew_lock_on_session_of_the_sessionful_entity(): ) as receiver: # automatically renew the lock on the session for 100 seconds - renewer.register(receiver.session, timeout=100) + renewer.register(receiver, receiver.session, timeout=100) print('Register session into AutoLockRenewer.') received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5) time.sleep(100) # message handling for long period (E.g. application logic) for msg in received_msgs: - msg.complete() + receiver.complete_message(msg) print('Complete messages.') @@ -111,7 +111,7 @@ def on_lock_renew_failure_callback(renewable, error): for msg in received_msgs: # automatically renew the lock on each message for 120 seconds - renewer.register(msg, timeout=90, on_lock_renew_failure=on_lock_renew_failure_callback) + renewer.register(receiver, msg, timeout=90, on_lock_renew_failure=on_lock_renew_failure_callback) print('Register messages into AutoLockRenewer done.') # Cause the messages and autorenewal to time out. @@ -120,7 +120,7 @@ def on_lock_renew_failure_callback(renewable, error): try: for msg in received_msgs: - msg.complete() + receiver.complete_message(msg) except MessageLockExpired as e: print('Messages cannot be settled if they have timed out. (This is expected)') @@ -129,4 +129,4 @@ def on_lock_renew_failure_callback(renewable, error): renew_lock_on_message_received_from_non_sessionful_entity() renew_lock_on_session_of_the_sessionful_entity() -renew_lock_with_lock_renewal_failure_callback() \ No newline at end of file +renew_lock_with_lock_renewal_failure_callback() diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deadlettered_messages.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deadlettered_messages.py index 789dc8181c96..a923d1533c91 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deadlettered_messages.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deadlettered_messages.py @@ -31,7 +31,7 @@ received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - msg.dead_letter() + receiver.dead_letter_message(msg) print('receiving deadlettered messages') dlq_receiver = servicebus_client.get_queue_receiver(queue_name=QUEUE_NAME, sub_queue=SubQueue.DeadLetter) @@ -39,6 +39,6 @@ received_msgs = dlq_receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - msg.complete() + dlq_receiver.complete_message(msg) print("Receive is done.") diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deferred_message_queue.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deferred_message_queue.py index d4757afe5f5c..0e2e8691102c 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deferred_message_queue.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_deferred_message_queue.py @@ -32,7 +32,7 @@ for msg in received_msgs: print("Deferring msg: {}".format(str(msg))) deferred_sequenced_numbers.append(msg.sequence_number) - msg.defer() + receiver.defer_message(msg) if deferred_sequenced_numbers: received_deferred_msg = receiver.receive_deferred_messages( @@ -41,7 +41,7 @@ for msg in received_deferred_msg: print("Completing deferred msg: {}".format(str(msg))) - msg.complete() + receiver.complete_message(msg) else: print("No messages received.") diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_iterator_queue.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_iterator_queue.py index 0bd9e52cafb7..a9e1906b989a 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_iterator_queue.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_iterator_queue.py @@ -24,6 +24,6 @@ with receiver: for msg in receiver: print(str(msg)) - msg.complete() + receiver.complete_message(msg) print("Receive is done.") diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_queue.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_queue.py index 50eedb8a9ca8..8936a966c22c 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_queue.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_queue.py @@ -25,6 +25,6 @@ received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - msg.complete() + receiver.complete_message(msg) print("Receive is done.") diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_subscription.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_subscription.py index a43921e11760..2b97fcaf6dc5 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_subscription.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/receive_subscription.py @@ -29,6 +29,6 @@ received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - msg.complete() + receiver.complete_message(msg) print("Receive is done.") diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/sample_code_servicebus.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/sample_code_servicebus.py index c20f09fbe3e6..a4edf2546694 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/sample_code_servicebus.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/sample_code_servicebus.py @@ -224,7 +224,7 @@ def example_send_and_receive_sync(): # Auto renew message for 1 minute. lock_renewal.register(message, timeout=60) process_message(message) - message.complete() + servicebus_receiver.complete_message(message) # [END auto_lock_renew_message_sync] break @@ -233,7 +233,7 @@ def example_send_and_receive_sync(): messages = servicebus_receiver.receive_messages(max_wait_time=5) for message in messages: print(str(message)) - message.complete() + servicebus_receiver.complete_message(message) # [END receive_sync] # [START receive_complex_message] @@ -255,15 +255,16 @@ def example_send_and_receive_sync(): # [START abandon_message] messages = servicebus_receiver.receive_messages(max_wait_time=5) for message in messages: - message.abandon() + servicebus_receiver.abandon_message(message) # [END abandon_message] # [START receive_forever] with servicebus_receiver: for message in servicebus_receiver.get_streaming_message_iter(): print(str(message)) - message.complete() + servicebus_receiver.complete_message(message) # [END receive_forever] + break def example_receive_deferred_sync(): @@ -278,14 +279,14 @@ def example_receive_deferred_sync(): for message in messages: deferred_sequenced_numbers.append(message.sequence_number) print(str(message)) - message.defer() + servicebus_receiver.defer_message(message) received_deferred_msg = servicebus_receiver.receive_deferred_messages( sequence_numbers=deferred_sequenced_numbers ) for msg in received_deferred_msg: - msg.complete() + servicebus_receiver.complete_message(msg) # [END receive_defer_sync] @@ -300,12 +301,12 @@ def example_receive_deadletter_sync(): with servicebus_client.get_queue_receiver(queue_name) as servicebus_receiver: messages = servicebus_receiver.receive_messages(max_wait_time=5) for message in messages: - message.dead_letter(reason='reason for dead lettering', description='description for dead lettering') + servicebus_receiver.dead_letter_message(message, reason='reason for dead lettering', description='description for dead lettering') with servicebus_client.get_queue_deadletter_receiver(queue_name) as servicebus_deadletter_receiver: messages = servicebus_deadletter_receiver.receive_messages(max_wait_time=5) for message in messages: - message.complete() + servicebus_receiver.complete_message(message) # [END receive_deadletter_sync] @@ -345,10 +346,10 @@ def example_session_ops_sync(): with servicebus_client.get_queue_receiver(queue_name=queue_name, session_id=session_id) as receiver: session = receiver.session # Auto renew session lock for 2 minutes - lock_renewal.register(session, timeout=120) + lock_renewal.register(receiver, session, timeout=120) for message in receiver: process_message(message) - message.complete() + receiver.complete_message(message) # [END auto_lock_renew_session_sync] break diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/session_pool_receive.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/session_pool_receive.py index a92166c60d31..c45fa8ff9f8e 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/session_pool_receive.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/session_pool_receive.py @@ -21,7 +21,7 @@ def message_processing(sb_client, queue_name, messages): try: with sb_client.get_queue_receiver(queue_name, max_wait_time=1, session_id=NEXT_AVAILABLE_SESSION) as receiver: renewer = AutoLockRenewer() - renewer.register(receiver.session) + renewer.register(receiver, receiver.session) receiver.session.set_state("OPEN") for message in receiver: messages.append(message) @@ -33,7 +33,7 @@ def message_processing(sb_client, queue_name, messages): print("Locked until: {}".format(message.locked_until_utc)) print("Lock Token: {}".format(message.lock_token)) print("Enqueued time: {}".format(message.enqueued_time_utc)) - message.complete() + receiver.complete_message(message) if str(message) == 'shutdown': receiver.session.set_state("CLOSED") renewer.close() diff --git a/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py b/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py index 7c639187e51a..1221cfdff229 100644 --- a/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py +++ b/sdk/servicebus/azure-servicebus/samples/sync_samples/session_send_receive.py @@ -48,7 +48,7 @@ def receive_batch_message(receiver): received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=5) for msg in received_msgs: print(str(msg)) - msg.complete() + receiver.complete_message(msg) session.renew_lock() session.set_state("END") print("Session state:", session.get_state()) diff --git a/sdk/servicebus/azure-servicebus/tests/async_tests/mocks_async.py b/sdk/servicebus/azure-servicebus/tests/async_tests/mocks_async.py index fd3f70b06c9b..6b35e2803f02 100644 --- a/sdk/servicebus/azure-servicebus/tests/async_tests/mocks_async.py +++ b/sdk/servicebus/azure-servicebus/tests/async_tests/mocks_async.py @@ -6,6 +6,13 @@ class MockReceiver: def __init__(self): self._running = True + async def renew_message_lock(self, message): + if message._exception_on_renew_lock: + raise Exception("Generated exception via MockReceivedMessage exception_on_renew_lock") + if not message._prevent_renew_lock: + message.locked_until_utc = message.locked_until_utc + timedelta(seconds=message._lock_duration) + + class MockReceivedMessage: def __init__(self, prevent_renew_lock=False, exception_on_renew_lock=False): self._lock_duration = 2 @@ -18,13 +25,6 @@ def __init__(self, prevent_renew_lock=False, exception_on_renew_lock=False): self._prevent_renew_lock = prevent_renew_lock self._exception_on_renew_lock = exception_on_renew_lock - - async def renew_lock(self): - if self._exception_on_renew_lock: - raise Exception("Generated exception via MockReceivedMessage exception_on_renew_lock") - if not self._prevent_renew_lock: - self.locked_until_utc = self.locked_until_utc + timedelta(seconds=self._lock_duration) - @property def _lock_expired(self): if self.locked_until_utc and self.locked_until_utc <= utc_now(): diff --git a/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py b/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py index 7b6efe2f73e4..17e6850489f0 100644 --- a/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py +++ b/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py @@ -19,10 +19,16 @@ from uamqp import compat from azure.servicebus.aio import ( ServiceBusClient, + AutoLockRenewer +) +from azure.servicebus import ( + ServiceBusMessage, + ServiceBusMessageBatch, ServiceBusReceivedMessage, - AutoLockRenewer) -from azure.servicebus import TransportType -from azure.servicebus._common.message import ServiceBusMessage, ServiceBusMessageBatch, ServiceBusPeekedMessage + TransportType, + ReceiveMode, + SubQueue +) from azure.servicebus._common.constants import ReceiveMode, SubQueue from azure.servicebus._common.utils import utc_now from azure.servicebus.exceptions import ( @@ -68,7 +74,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_peeklock(sel async for message in receiver: print_message(_logger, message) count += 1 - await message.complete() + await receiver.complete_message(message) assert count == 10 @@ -93,7 +99,7 @@ async def test_async_queue_by_queue_client_send_multiple_messages(self, serviceb async for message in receiver: print_message(_logger, message) count += 1 - await message.complete() + await receiver.complete_message(message) assert count == 10 @@ -128,14 +134,14 @@ async def test_github_issue_6178_async(self, servicebus_namespace_connection_str async with sb_client.get_queue_sender(servicebus_queue.name) as sender: for i in range(3): - await sender.send_messages(ServiceBusMessage("ServiceBusMessage {}".format(i))) - async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=60) as messages: - async for message in messages: + await sender.send_messages(ServiceBusMessage("Message {}".format(i))) + async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=60) as receiver: + async for message in receiver: _logger.debug(message) _logger.debug(message.sequence_number) _logger.debug(message.enqueued_time_utc) _logger.debug(message._lock_expired) - await message.complete() + await receiver.complete_message(message) await asyncio.sleep(40) @pytest.mark.liveTest @@ -157,7 +163,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_receiveandde async for message in receiver: messages.append(message) with pytest.raises(MessageAlreadySettled): - await message.complete() + await receiver.complete_message(message) assert not receiver._running assert len(messages) == 10 @@ -188,7 +194,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_stop(se async with receiver: async for message in receiver: messages.append(message) - await message.complete() + await receiver.complete_message(message) if len(messages) >= 5: break @@ -198,7 +204,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_stop(se async with receiver: async for message in receiver: messages.append(message) - await message.complete() + await receiver.complete_message(message) if len(messages) >= 5: break @@ -224,11 +230,11 @@ async def test_async_queue_by_servicebus_client_iter_messages_simple(self, servi count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - await message.complete() + await receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - await message.renew_lock() + await receiver.renew_message_lock(message) count += 1 with pytest.raises(StopAsyncIteration): @@ -257,10 +263,10 @@ async def test_async_queue_by_servicebus_conn_str_client_iter_messages_with_aban print_message(_logger, message) if not message.delivery_count: count += 1 - await message.abandon() + await receiver.abandon_message(message) else: assert message.delivery_count == 1 - await message.complete() + await receiver.complete_message(message) assert count == 10 @@ -268,7 +274,7 @@ async def test_async_queue_by_servicebus_conn_str_client_iter_messages_with_aban count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 0 @@ -294,14 +300,14 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_defer(self, s deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5, receive_mode=ReceiveMode.PeekLock) as receiver: count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 0 @@ -327,7 +333,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 @@ -335,7 +341,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - await message.complete() + await receiver.complete_message(message) with pytest.raises(ServiceBusError): await receiver.receive_deferred_messages(deferred_messages) @@ -360,21 +366,21 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 - async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5) as session: + async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5) as receiver: with pytest.raises(ValueError): - await session.receive_deferred_messages(deferred_messages, timeout=0) - deferred = await session.receive_deferred_messages(deferred_messages) + await receiver.receive_deferred_messages(deferred_messages, timeout=0) + deferred = await receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) assert message.lock_token assert message.locked_until_utc assert message._receiver - await message.renew_lock() - await message.complete() + await receiver.renew_message_lock(message) + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -396,16 +402,16 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 - async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5) as session: - deferred = await session.receive_deferred_messages(deferred_messages, timeout=None) + async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5) as receiver: + deferred = await receiver.receive_deferred_messages(deferred_messages, timeout=None) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") count = 0 async with sb_client.get_queue_receiver(servicebus_queue.name, @@ -418,7 +424,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - await message.complete() + await receiver.complete_message(message) assert count == 10 @pytest.mark.liveTest @@ -441,7 +447,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 async with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5, receive_mode=ReceiveMode.ReceiveAndDelete) as receiver: @@ -450,7 +456,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) with pytest.raises(MessageAlreadySettled): - await message.complete() + await receiver.complete_message(message) with pytest.raises(ServiceBusError): deferred = await receiver.receive_deferred_messages(deferred_messages) @@ -476,7 +482,7 @@ async def test_async_queue_by_servicebus_client_iter_messages_with_retrieve_defe deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 3 @@ -509,7 +515,7 @@ async def test_async_queue_by_servicebus_client_receive_batch_with_deadletter(se for message in messages: print_message(_logger, message) count += 1 - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") messages = await receiver.receive_messages() assert count == 10 @@ -518,7 +524,7 @@ async def test_async_queue_by_servicebus_client_receive_batch_with_deadletter(se count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 0 @@ -529,7 +535,7 @@ async def test_async_queue_by_servicebus_client_receive_batch_with_deadletter(se mode=ReceiveMode.PeekLock) as dl_receiver: count = 0 async for message in dl_receiver: - await message.complete() + await dl_receiver.complete_message(message) count += 1 assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description' @@ -558,7 +564,7 @@ async def test_async_queue_by_servicebus_client_receive_batch_with_retrieve_dead while messages: for message in messages: print_message(_logger, message) - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") count += 1 messages = await receiver.receive_messages() @@ -577,7 +583,7 @@ async def test_async_queue_by_servicebus_client_receive_batch_with_retrieve_dead assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 10 @@ -613,11 +619,11 @@ async def test_async_queue_by_servicebus_client_browse_messages_client(self, ser async with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = await receiver.peek_messages(5) assert len(messages) == 5 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() + with pytest.raises(MessageSettleFailed): + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -636,11 +642,11 @@ async def test_async_queue_by_servicebus_client_browse_messages_with_receiver(se messages = await receiver.peek_messages(5, timeout=5) assert len(messages) > 0 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() + with pytest.raises(MessageSettleFailed): + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -681,17 +687,17 @@ async def test_async_queue_by_servicebus_client_renew_message_locks(self, servic try: with pytest.raises(AttributeError): assert not message._lock_expired - for m in messages: + for message in messages: time.sleep(5) - initial_expiry = m.locked_until_utc - await m.renew_lock() - assert (m.locked_until_utc - initial_expiry) >= timedelta(seconds=5) + initial_expiry = message.locked_until_utc + await receiver.renew_message_lock(message) + assert (message.locked_until_utc - initial_expiry) >= timedelta(seconds=5) finally: - await messages[0].complete() - await messages[1].complete() + await receiver.complete_message(messages[0]) + await receiver.complete_message(messages[1]) sleep_until_expired(messages[2]) with pytest.raises(MessageLockExpired): - await messages[2].complete() + await receiver.complete_message(messages[2]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -714,7 +720,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_autoloc if not messages: messages.append(message) assert not message._lock_expired - renewer.register(message, timeout=60) + renewer.register(receiver, message, timeout=60) print("Registered lock renew thread", message.locked_until_utc, utc_now()) await asyncio.sleep(60) print("Finished first sleep", message.locked_until_utc) @@ -724,7 +730,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_autoloc print("Finished second sleep", message.locked_until_utc, utc_now()) assert message._lock_expired try: - await message.complete() + await receiver.complete_message(message) raise AssertionError("Didn't raise MessageLockExpired") except MessageLockExpired as e: assert isinstance(e.inner_exception, AutoLockRenewTimeout) @@ -733,12 +739,12 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_autoloc print("Remaining messages", message.locked_until_utc, utc_now()) assert message._lock_expired with pytest.raises(MessageLockExpired): - await message.complete() + await receiver.complete_message(message) else: assert message.delivery_count >= 1 print("Remaining messages", message.locked_until_utc, utc_now()) messages.append(message) - await message.complete() + await receiver.complete_message(message) await renewer.close() assert len(messages) == 11 @@ -789,7 +795,7 @@ async def test_async_queue_message_time_to_live(self, servicebus_namespace_conne count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 1 @@ -815,7 +821,7 @@ async def test_async_queue_message_duplicate_detection(self, servicebus_namespac async for message in receiver: print_message(_logger, message) assert message.message_id == message_id - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 1 @@ -838,7 +844,7 @@ async def test_async_queue_message_connection_closed(self, servicebus_namespace_ assert len(messages) == 1 with pytest.raises(MessageSettleFailed): - await messages[0].complete() + await receiver.complete_message(messages[0]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -860,16 +866,16 @@ async def test_async_queue_message_expiry(self, servicebus_namespace_connection_ time.sleep(60) assert messages[0]._lock_expired with pytest.raises(MessageLockExpired): - await messages[0].complete() + await receiver.complete_message(messages[0]) with pytest.raises(MessageLockExpired): - await messages[0].renew_lock() + await receiver.renew_message_lock(messages[0]) async with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = await receiver.receive_messages(max_wait_time=30) assert len(messages) == 1 print_message(_logger, messages[0]) assert messages[0].delivery_count > 0 - await messages[0].complete() + await receiver.complete_message(messages[0]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -889,12 +895,12 @@ async def test_async_queue_message_lock_renew(self, servicebus_namespace_connect messages = await receiver.receive_messages(max_wait_time=10) assert len(messages) == 1 time.sleep(15) - await messages[0].renew_lock(timeout=5) + await receiver.renew_message_lock(messages[0], timeout=5) time.sleep(15) - await messages[0].renew_lock() + await receiver.renew_message_lock(messages[0]) time.sleep(15) assert not messages[0]._lock_expired - await messages[0].complete() + await receiver.complete_message(messages[0]) async with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = await receiver.receive_messages(max_wait_time=10) @@ -916,18 +922,18 @@ async def test_async_queue_message_receive_and_delete(self, servicebus_namespace async with sb_client.get_queue_receiver(servicebus_queue.name, receive_mode=ReceiveMode.ReceiveAndDelete) as receiver: messages = await receiver.receive_messages(max_wait_time=10) assert len(messages) == 1 - received = messages[0] - print_message(_logger, received) + message = messages[0] + print_message(_logger, message) with pytest.raises(MessageAlreadySettled): - await received.complete() + await receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - await received.abandon() + await receiver.abandon_message(message) with pytest.raises(MessageAlreadySettled): - await received.defer() + await receiver.defer_message(message) with pytest.raises(MessageAlreadySettled): - await received.dead_letter() + await receiver.dead_letter_message(message) with pytest.raises(MessageAlreadySettled): - await received.renew_lock() + await receiver.renew_message_lock(message) time.sleep(30) async with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: @@ -958,7 +964,7 @@ async def test_async_queue_message_batch(self, servicebus_namespace_connection_s messages.extend(recv) for message in recv: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) recv = await receiver.receive_messages(max_wait_time=10) assert len(messages) == 5 @@ -992,8 +998,8 @@ async def test_async_queue_schedule_message(self, servicebus_namespace_connectio assert messages[0].scheduled_enqueue_time_utc <= messages[0].enqueued_time_utc.replace(microsecond=0) assert len(messages) == 1 finally: - for m in messages: - await m.complete() + for message in messages: + await receiver.complete_message(message) else: raise Exception("Failed to receive scheduled message.") @@ -1023,7 +1029,7 @@ async def test_async_queue_schedule_multiple_messages(self, servicebus_namespace received_messages = [] async for message in receiver.get_streaming_message_iter(max_wait_time=5): received_messages.append(message) - await message.complete() + await receiver.complete_message(message) tokens = await sender.schedule_messages(received_messages, scheduled_enqueue_time, timeout=5) assert len(tokens) == 2 @@ -1040,8 +1046,8 @@ async def test_async_queue_schedule_multiple_messages(self, servicebus_namespace assert messages[0].scheduled_enqueue_time_utc <= messages[0].enqueued_time_utc.replace(microsecond=0) assert len(messages) == 2 finally: - for m in messages: - await m.complete() + for message in messages: + await receiver.complete_message(message) else: raise Exception("Failed to receive scheduled message.") @@ -1127,8 +1133,7 @@ async def test_queue_message_settle_through_mgmt_link_due_to_broken_receiver_lin messages = await receiver.receive_messages(max_wait_time=5) await receiver._handler.message_handler.destroy_async() # destroy the underlying receiver link assert len(messages) == 1 - await messages[0].complete() - + await receiver.complete_message(messages[0]) @pytest.mark.asyncio async def test_async_queue_mock_auto_lock_renew_callback(self): @@ -1278,12 +1283,12 @@ def message_content(): print_message(_logger, message) if message.label == '1st': message_1st_received_cnt += 1 - await message.complete() + await receiver.complete_message(message) message.label = '2nd' await sender.send_messages(message) # resending received message elif message.label == '2nd': message_2nd_received_cnt += 1 - await message.complete() + await receiver.complete_message(message) assert message_1st_received_cnt == 20 and message_2nd_received_cnt == 20 # Network/server might be unstable making flow control ineffective in the leading rounds of connection iteration @@ -1314,8 +1319,8 @@ async def test_async_queue_receiver_alive_after_timeout(self, servicebus_namespa async for message in receiver.get_streaming_message_iter(): messages.append(message) - for m in messages: - await m.complete() + for message in messages: + await receiver.complete_message(message) assert len(messages) == 2 assert str(messages[0]) == "0" @@ -1334,8 +1339,8 @@ async def test_async_queue_receiver_alive_after_timeout(self, servicebus_namespa assert str(messages[2]) == "2" assert str(messages[3]) == "3" - for m in messages[2:]: - await m.complete() + for message in messages[2:]: + await receiver.complete_message(message) messages = await receiver.receive_messages() assert not messages @@ -1362,9 +1367,9 @@ async def test_queue_receive_keep_conn_alive_async(self, servicebus_namespace_co receiver_handler = receiver._handler assert len(messages) == 2 await asyncio.sleep(4 * 60 + 5) # 240s is the service defined connection idle timeout - await messages[0].renew_lock() # check mgmt link operation - await messages[0].complete() - await messages[1].complete() # check receiver link operation + await receiver.renew_message_lock(messages[0]) # check mgmt link operation + await receiver.complete_message(messages[0]) + await receiver.complete_message(messages[1]) # check receiver link operation await asyncio.sleep(60) # sleep another one minute to ensure we pass the lock_duration time messages = [] @@ -1394,7 +1399,7 @@ async def test_async_queue_receiver_respects_max_wait_time_overrides(self, servi time_1 = receiver._handler._counter.get_current_ms() async for message in receiver.get_streaming_message_iter(max_wait_time=10): messages.append(message) - await message.complete() + await receiver.complete_message(message) time_2 = receiver._handler._counter.get_current_ms() async for message in receiver.get_streaming_message_iter(max_wait_time=1): @@ -1514,7 +1519,6 @@ async def hack_mgmt_execute_async(self, operation, op_type, message, timeout=0): # must reset the mgmt execute method, otherwise other test cases would use the hacked execute method, leading to timeout error uamqp.async_ops.mgmt_operation_async.MgmtOperationAsync.execute_async = original_execute_method - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -1527,7 +1531,7 @@ def _hack_amqp_message_complete(cls): async def _hack_amqp_mgmt_request(cls, message, operation, op_type=None, node=None, callback=None, **kwargs): raise uamqp.errors.AMQPConnectionError() - async def _hack_sb_message_settle_message(self, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): + async def _hack_sb_receiver_settle_message(self, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): raise uamqp.errors.AMQPError() async with ServiceBusClient.from_connection_string( @@ -1539,7 +1543,7 @@ async def _hack_sb_message_settle_message(self, settle_operation, dead_letter_re await sender.send_messages(ServiceBusMessage("body"), timeout=5) message = (await receiver.receive_messages(max_wait_time=5))[0] message.message.accept = types.MethodType(_hack_amqp_message_complete, message.message) - await message.complete() # settle via mgmt link + await receiver.complete_message(message) # settle via mgmt link try: origin_amqp_mgmt_request_method = receiver._handler.mgmt_request_async @@ -1552,12 +1556,14 @@ async def _hack_sb_message_settle_message(self, settle_operation, dead_letter_re await sender.send_messages(ServiceBusMessage("body"), timeout=5) message = (await receiver.receive_messages(max_wait_time=5))[0] - message._settle_message = types.MethodType(_hack_sb_message_settle_message, message) + origin_sb_receiver_settle_message_method = receiver._settle_message + receiver._settle_message = types.MethodType(_hack_sb_receiver_settle_message, receiver) with pytest.raises(MessageSettleFailed): - await message.complete() + await receiver.complete_message(message) + receiver._settle_message = origin_sb_receiver_settle_message_method message = (await receiver.receive_messages(max_wait_time=6))[0] - await message.complete() + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -1575,4 +1581,4 @@ async def test_async_send_message_no_body(self, servicebus_namespace_connection_ max_wait_time=10) as receiver: message = await receiver.__anext__() assert message.body is None - await message.complete() + await receiver.complete_message(message) diff --git a/sdk/servicebus/azure-servicebus/tests/async_tests/test_sessions_async.py b/sdk/servicebus/azure-servicebus/tests/async_tests/test_sessions_async.py index 3d1a3fb2b344..64e86d266772 100644 --- a/sdk/servicebus/azure-servicebus/tests/async_tests/test_sessions_async.py +++ b/sdk/servicebus/azure-servicebus/tests/async_tests/test_sessions_async.py @@ -16,13 +16,12 @@ from uamqp.errors import VendorLinkDetach from azure.servicebus import ( ServiceBusMessage, - ServiceBusPeekedMessage, ServiceBusReceivedMessage, ReceiveMode, NEXT_AVAILABLE_SESSION, SubQueue ) -from azure.servicebus.aio import ServiceBusClient, ServiceBusReceivedMessage, AutoLockRenewer +from azure.servicebus.aio import ServiceBusClient, AutoLockRenewer from azure.servicebus._common.utils import utc_now from azure.servicebus.exceptions import ( ServiceBusConnectionError, @@ -68,19 +67,18 @@ async def test_async_session_by_session_client_conn_str_receive_handler_peeklock with pytest.raises(ServiceBusConnectionError): await sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5)._open_with_retry() - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) count = 0 - async for message in session: + async for message in receiver: print_message(_logger, message) assert message.session_id == session_id count += 1 - await message.complete() + await receiver.complete_message(message) - await session.close() + await receiver.close() assert count == 3 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -97,28 +95,27 @@ async def test_async_session_by_queue_client_conn_str_receive_handler_receiveand await sender.send_messages(message) messages = [] - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) - async for message in session: + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) + async for message in receiver: messages.append(message) - assert session_id == session.session.session_id + assert session_id == receiver.session.session_id assert session_id == message.session_id with pytest.raises(MessageAlreadySettled): - await message.complete() + await receiver.complete_message(message) - assert session._running - await session.close() + assert receiver._running + await receiver.close() - assert not session._running + assert not receiver._running assert len(messages) == 10 time.sleep(30) messages = [] - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) as session: - async for message in session: + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) as receiver: + async for message in receiver: messages.append(message) assert len(messages) == 0 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -135,32 +132,31 @@ async def test_async_session_by_session_client_conn_str_receive_handler_with_sto await sender.send_messages(message) messages = [] - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) - async with session: - async for message in session: - assert session_id == session.session.session_id + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) + async with receiver: + async for message in receiver: + assert session_id == receiver.session.session_id assert session_id == message.session_id messages.append(message) - await message.complete() + await receiver.complete_message(message) if len(messages) >= 5: break - assert session._running + assert receiver._running assert len(messages) == 5 - async with session: - async for message in session: - assert session_id == session.session.session_id + async with receiver: + async for message in receiver: + assert session_id == receiver.session.session_id assert session_id == message.session_id messages.append(message) - await message.complete() + await receiver.complete_message(message) if len(messages) >= 5: break - assert not session._running + assert not receiver._running assert len(messages) == 6 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -170,10 +166,9 @@ async def test_async_session_by_session_client_conn_str_receive_handler_with_no_ async with ServiceBusClient.from_connection_string( servicebus_namespace_connection_string, logging_enable=False) as sb_client: - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) with pytest.raises(NoActiveSession): - await session._open_with_retry() - + await receiver._open_with_retry() @pytest.mark.liveTest @pytest.mark.live_test_only @@ -186,15 +181,14 @@ async def test_async_session_by_session_client_conn_str_receive_handler_with_ina session_id = str(uuid.uuid4()) messages = [] - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) - async with session: - async for message in session: + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, max_wait_time=5) + async with receiver: + async for message in receiver: messages.append(message) - assert not session._running + assert not receiver._running assert len(messages) == 0 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -211,17 +205,17 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de await sender.send_messages(message) count = 0 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - async for message in session: + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + async for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - deferred = await session.receive_deferred_messages(deferred_messages) + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + deferred = await receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) @@ -229,8 +223,8 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de assert not message.locked_until_utc assert message._receiver with pytest.raises(TypeError): - await message.renew_lock() - await message.complete() + await receiver.renew_message_lock(message) + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -248,21 +242,21 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de await sender.send_messages(message) count = 0 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - async for message in session: + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + async for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - deferred = await session.receive_deferred_messages(deferred_messages) + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + deferred = await receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") count = 0 async with sb_client.get_queue_receiver(servicebus_queue.name, @@ -275,7 +269,7 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - await message.complete() + await receiver.complete_message(message) assert count == 10 @pytest.mark.liveTest @@ -294,24 +288,23 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de await sender.send_messages(message) count = 0 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - async for message in session: + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + async for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() + await receiver.defer_message(message) assert count == 10 - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.ReceiveAndDelete) as session: - deferred = await session.receive_deferred_messages(deferred_messages) + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.ReceiveAndDelete) as receiver: + deferred = await receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) with pytest.raises(MessageAlreadySettled): - await message.complete() + await receiver.complete_message(message) with pytest.raises(ServiceBusError): - deferred = await session.receive_deferred_messages(deferred_messages) - + deferred = await receiver.receive_deferred_messages(deferred_messages) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -329,19 +322,19 @@ async def test_async_session_by_servicebus_client_iter_messages_with_retrieve_de message = ServiceBusMessage("Deferred message no. {}".format(i), session_id=session_id) await sender.send_messages(message) - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) count = 0 - async for message in session: + async for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - await message.defer() - await session.close() + await receiver.defer_message(message) + await receiver.close() assert count == 10 with pytest.raises(MessageSettleFailed): - await message.complete() + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -365,26 +358,26 @@ async def test_async_session_by_servicebus_client_fetch_next_with_retrieve_deadl while messages: for message in messages: print_message(_logger, message) - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", + error_description="Testing description") count += 1 messages = await receiver.receive_messages() assert count == 10 async with sb_client.get_queue_receiver(servicebus_queue.name, sub_queue = SubQueue.DeadLetter, - max_wait_time=5) as session: + max_wait_time=5) as receiver: count = 0 - async for message in session: + async for message in receiver: print_message(_logger, message) assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 10 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -408,11 +401,11 @@ async def test_async_session_by_servicebus_client_browse_messages_client(self, s async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id) as receiver: messages = await receiver.peek_messages(5) assert len(messages) == 5 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() + with pytest.raises(MessageSettleFailed): + await receiver.complete_message(message) async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id_2) as receiver: messages = await receiver.peek_messages(5) @@ -436,12 +429,11 @@ async def test_async_session_by_servicebus_client_browse_messages_with_receiver( messages = await receiver.peek_messages(5) assert len(messages) > 0 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() - + with pytest.raises(MessageSettleFailed): + await receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -478,12 +470,11 @@ async def test_async_session_by_servicebus_client_renew_client_locks(self, servi await receiver.session.renew_lock(timeout=5) assert (receiver.session.locked_until_utc - initial_expiry) >= timedelta(seconds=5) finally: - await messages[0].complete() - await messages[1].complete() - time.sleep(70) #TODO: BUG: Was 40 + await receiver.complete_message(messages[0]) + await receiver.complete_message(messages[1]) + time.sleep(40) with pytest.raises(SessionLockExpired): - await messages[2].complete() - + await receiver.complete_message(messages[2]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -506,32 +497,32 @@ async def lock_lost_callback(renewable, error): renewer = AutoLockRenewer() messages = [] - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.PeekLock, prefetch_count=20) as session: - renewer.register(session.session, timeout=60) - print("Registered lock renew thread", session.session.locked_until_utc, utc_now()) + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.PeekLock, prefetch_count=20) as receiver: + renewer.register(receiver, receiver.session, timeout=60) + print("Registered lock renew thread", receiver.session.locked_until_utc, utc_now()) with pytest.raises(SessionLockExpired): - async for message in session: + async for message in receiver: if not messages: await asyncio.sleep(45) - print("First sleep {}".format(session.session.locked_until_utc - utc_now())) - assert not session.session._lock_expired + print("First sleep {}".format(receiver.session.locked_until_utc - utc_now())) + assert not receiver.session._lock_expired with pytest.raises(TypeError): message._lock_expired assert message.locked_until_utc is None with pytest.raises(TypeError): - await message.renew_lock() + await receiver.renew_message_lock(message) assert message.lock_token is not None - await message.complete() + await receiver.complete_message(message) messages.append(message) elif len(messages) == 1: assert not results await asyncio.sleep(45) - print("Second sleep {}".format(session.session.locked_until_utc - utc_now())) - assert session.session._lock_expired - assert isinstance(session.session.auto_renew_error, AutoLockRenewTimeout) + print("Second sleep {}".format(receiver.session.locked_until_utc - utc_now())) + assert receiver.session._lock_expired + assert isinstance(receiver.session.auto_renew_error, AutoLockRenewTimeout) try: - await message.complete() + await receiver.complete_message(message) raise AssertionError("Didn't raise SessionLockExpired") except SessionLockExpired as e: assert isinstance(e.inner_exception, AutoLockRenewTimeout) @@ -543,7 +534,7 @@ async def lock_lost_callback(renewable, error): async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.PeekLock, prefetch_count=10) as receiver: session = receiver.session - renewer.register(session, timeout=5, on_lock_renew_failure=lock_lost_callback) + renewer.register(receiver, session, timeout=5, on_lock_renew_failure=lock_lost_callback) await asyncio.sleep(max(0,(session.locked_until_utc - utc_now()).total_seconds()+1)) # If this pattern repeats make sleep_until_expired_async assert not results @@ -572,7 +563,7 @@ async def test_async_session_message_connection_closed(self, servicebus_namespac assert len(messages) == 1 with pytest.raises(MessageSettleFailed): - await messages[0].complete() + await receiver.complete_message(messages[0]) @pytest.mark.liveTest @@ -599,10 +590,10 @@ async def test_async_session_message_expiry(self, servicebus_namespace_connectio with pytest.raises(TypeError): messages[0]._lock_expired with pytest.raises(TypeError): - await messages[0].renew_lock() + await receiver.renew_message_lock(messages[0]) assert receiver.session._lock_expired with pytest.raises(SessionLockExpired): - await messages[0].complete() + await receiver.complete_message(messages[0]) with pytest.raises(SessionLockExpired): await receiver.session.renew_lock() @@ -611,8 +602,7 @@ async def test_async_session_message_expiry(self, servicebus_namespace_connectio assert len(messages) == 1 print_message(_logger, messages[0]) assert messages[0].delivery_count - await messages[0].complete() - + await receiver.complete_message(messages[0]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -636,7 +626,7 @@ async def test_async_session_schedule_message(self, servicebus_namespace_connect messages = [] renewer = AutoLockRenewer() async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id) as receiver: - renewer.register(receiver.session, timeout=140) + renewer.register(receiver, receiver.session, timeout=140) messages.extend(await receiver.receive_messages(max_wait_time=120)) messages.extend(await receiver.receive_messages(max_wait_time=5)) if messages: @@ -676,7 +666,7 @@ async def test_async_session_schedule_multiple_messages(self, servicebus_namespa renewer = AutoLockRenewer() async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, prefetch_count=20) as receiver: - renewer.register(receiver.session, timeout=140) + renewer.register(receiver, receiver.session, timeout=140) messages.extend(await receiver.receive_messages(max_wait_time=120)) messages.extend(await receiver.receive_messages(max_wait_time=5)) if messages: @@ -690,7 +680,6 @@ async def test_async_session_schedule_multiple_messages(self, servicebus_namespa raise Exception("Failed to receive schdeduled message.") await renewer.close() - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -712,15 +701,15 @@ async def test_async_session_cancel_scheduled_messages(self, servicebus_namespac renewer = AutoLockRenewer() messages = [] async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id) as receiver: - renewer.register(receiver.session, timeout=140) + renewer.register(receiver, receiver.session, timeout=140) messages.extend(await receiver.receive_messages(max_wait_time=120)) messages.extend(await receiver.receive_messages(max_wait_time=5)) try: assert len(messages) == 0 except AssertionError: - for m in messages: - print(str(m)) - await m.complete() + for message in messages: + print(str(message)) + await receiver.complete_message(message) raise await renewer.close() @@ -740,18 +729,17 @@ async def test_async_session_get_set_state_with_receiver(self, servicebus_namesp message = ServiceBusMessage("Handler message no. {}".format(i), session_id=session_id) await sender.send_messages(message) - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - assert await session.session.get_state(timeout=5) == None - await session.session.set_state("first_state", timeout=5) + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + assert await receiver.session.get_state(timeout=5) == None + await receiver.session.set_state("first_state", timeout=5) count = 0 - async for m in session: + async for m in receiver: assert m.session_id == session_id count += 1 - state = await session.session.get_state() + state = await receiver.session.get_state() assert state == b'first_state' assert count == 3 - @pytest.mark.skip(reason='Requires list sessions') @pytest.mark.liveTest @pytest.mark.live_test_only @@ -781,7 +769,6 @@ async def test_async_session_by_servicebus_client_list_sessions_with_receiver(se assert len(current_sessions) == 5 assert current_sessions == sessions - @pytest.mark.skip(reason="requires list_session") @pytest.mark.liveTest @pytest.mark.live_test_only @@ -810,7 +797,6 @@ async def test_async_session_by_servicebus_client_list_sessions_with_client(self assert len(current_sessions) == 5 assert current_sessions == sessions - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -823,11 +809,11 @@ async def test_async_session_by_servicebus_client_session_pool(self, servicebus_ async def message_processing(sb_client): while True: try: - async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) as session: - async for message in session: + async with sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) as receiver: + async for message in receiver: print("ServiceBusMessage: {}".format(message)) messages.append(message) - await message.complete() + await receiver.complete_message(message) except NoActiveSession: return except Exception as e: @@ -873,7 +859,7 @@ async def test_async_session_basic_topic_subscription_send_and_receive(self, ser count = 0 async for message in receiver: count += 1 - await message.complete() + await receiver.complete_message(message) assert count == 1 @pytest.mark.liveTest diff --git a/sdk/servicebus/azure-servicebus/tests/async_tests/test_subscriptions_async.py b/sdk/servicebus/azure-servicebus/tests/async_tests/test_subscriptions_async.py index 1b00711eb8bd..485cc5de8d8d 100644 --- a/sdk/servicebus/azure-servicebus/tests/async_tests/test_subscriptions_async.py +++ b/sdk/servicebus/azure-servicebus/tests/async_tests/test_subscriptions_async.py @@ -55,7 +55,7 @@ async def test_subscription_by_subscription_client_conn_str_receive_basic(self, count = 0 async for message in receiver: count += 1 - await message.complete() + await receiver.complete_message(message) assert count == 1 @pytest.mark.liveTest @@ -87,7 +87,7 @@ async def test_subscription_by_sas_token_credential_conn_str_send_basic(self, se count = 0 async for message in receiver: count += 1 - await message.complete() + await receiver.complete_message(message) assert count == 1 @pytest.mark.liveTest @@ -122,7 +122,7 @@ async def test_topic_by_servicebus_client_receive_batch_with_deadletter(self, se for message in messages: print_message(_logger, message) count += 1 - await message.dead_letter(reason="Testing reason", error_description="Testing description") + await receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") messages = await receiver.receive_messages() assert count == 10 @@ -136,7 +136,7 @@ async def test_topic_by_servicebus_client_receive_batch_with_deadletter(self, se count = 0 async for message in receiver: print_message(_logger, message) - await message.complete() + await receiver.complete_message(message) count += 1 assert count == 0 @@ -149,7 +149,7 @@ async def test_topic_by_servicebus_client_receive_batch_with_deadletter(self, se ) as dl_receiver: count = 0 async for message in dl_receiver: - await message.complete() + await dl_receiver.complete_message(message) count += 1 assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description' diff --git a/sdk/servicebus/azure-servicebus/tests/mocks.py b/sdk/servicebus/azure-servicebus/tests/mocks.py index 4bfea86dbfb6..bb227e9e9d13 100644 --- a/sdk/servicebus/azure-servicebus/tests/mocks.py +++ b/sdk/servicebus/azure-servicebus/tests/mocks.py @@ -2,10 +2,18 @@ from azure.servicebus._common.utils import utc_now + class MockReceiver: def __init__(self): self._running = True + def renew_message_lock(self, message): + if message._exception_on_renew_lock: + raise Exception("Generated exception via MockReceivedMessage exception_on_renew_lock") + if not message._prevent_renew_lock: + message.locked_until_utc = message.locked_until_utc + timedelta(seconds=message._lock_duration) + + class MockReceivedMessage: def __init__(self, prevent_renew_lock=False, exception_on_renew_lock=False): self._lock_duration = 2 @@ -19,12 +27,6 @@ def __init__(self, prevent_renew_lock=False, exception_on_renew_lock=False): self._exception_on_renew_lock = exception_on_renew_lock - def renew_lock(self): - if self._exception_on_renew_lock: - raise Exception("Generated exception via MockReceivedMessage exception_on_renew_lock") - if not self._prevent_renew_lock: - self.locked_until_utc = self.locked_until_utc + timedelta(seconds=self._lock_duration) - @property def _lock_expired(self): if self.locked_until_utc and self.locked_until_utc <= utc_now(): diff --git a/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_base.py b/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_base.py index 4cf099b11646..0c29614e49b8 100644 --- a/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_base.py +++ b/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_base.py @@ -202,7 +202,7 @@ def _Receive(self, receiver, end_time): self.OnReceive(self._state, message) try: if self.should_complete_messages: - message.complete() + receiver.complete_message(message) except MessageAlreadySettled: # It may have been settled in the plugin callback. pass self._state.total_received += 1 diff --git a/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_queue_long_term_autorenew.py b/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_queue_long_term_autorenew.py index d3278c778cf0..99cae2b5baf5 100644 --- a/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_queue_long_term_autorenew.py +++ b/sdk/servicebus/azure-servicebus/tests/stress_tests/stress_test_queue_long_term_autorenew.py @@ -31,10 +31,10 @@ def receive_process_and_complete_message(client, queue_name): with queue_client.get_receiver() as queue_receiver: for message in queue_receiver: print("Received message: ", message) - lock_renewal.register(message, timeout=10800) + lock_renewal.register(queue_receiver, message, timeout=10800) process_message(message) print("Completing message") - message.complete() + queue_receiver.complete_message(message) break diff --git a/sdk/servicebus/azure-servicebus/tests/test_queues.py b/sdk/servicebus/azure-servicebus/tests/test_queues.py index b7efaf9b87f2..abf03b962a7e 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_queues.py +++ b/sdk/servicebus/azure-servicebus/tests/test_queues.py @@ -17,11 +17,17 @@ import uamqp import uamqp.errors from uamqp import compat -from azure.servicebus import ServiceBusClient, AutoLockRenewer, TransportType -from azure.servicebus._common.message import ServiceBusMessage, ServiceBusPeekedMessage, ServiceBusReceivedMessage, ServiceBusMessageBatch -from azure.servicebus._common.constants import ( +from azure.servicebus import ( + ServiceBusClient, + AutoLockRenewer, + TransportType, + ServiceBusMessage, + ServiceBusMessageBatch, + ServiceBusReceivedMessage, ReceiveMode, - SubQueue, + SubQueue +) +from azure.servicebus._common.constants import ( _X_OPT_LOCK_TOKEN, _X_OPT_PARTITION_KEY, _X_OPT_VIA_PARTITION_KEY, @@ -43,7 +49,7 @@ from devtools_testutils import AzureMgmtTestCase, CachedResourceGroupPreparer from servicebus_preparer import CachedServiceBusNamespacePreparer, ServiceBusQueuePreparer, CachedServiceBusQueuePreparer from utilities import get_logger, print_message, sleep_until_expired -from mocks import MockReceivedMessage +from mocks import MockReceivedMessage, MockReceiver _logger = get_logger(logging.DEBUG) @@ -98,7 +104,7 @@ def test_github_issue_6178(self, servicebus_namespace_connection_string, service _logger.debug(message.sequence_number) _logger.debug(message.enqueued_time_utc) _logger.debug(message._lock_expired) - message.complete() + receiver.complete_message(message) time.sleep(40) @@ -150,7 +156,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_peeklock(self, servicebu assert not message.session_id assert not message.reply_to_session_id count += 1 - message.complete() + receiver.complete_message(message) receiver.close() assert count == 10 @@ -198,7 +204,7 @@ def test_queue_by_queue_client_send_multiple_messages(self, servicebus_namespace assert not message.session_id assert not message.reply_to_session_id count += 1 - message.complete() + receiver.complete_message(message) assert count == 10 @@ -236,7 +242,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_receiveanddelete(self, s assert not message.reply_to_session_id messages.append(message) with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) assert len(messages) == 10 assert not receiver._running @@ -271,7 +277,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_with_stop(self, serviceb with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5) as receiver: for message in receiver: messages.append(message) - message.complete() + receiver.complete_message(message) if len(messages) >= 5: break @@ -281,7 +287,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_with_stop(self, serviceb with receiver: for message in receiver: messages.append(message) - message.complete() + receiver.complete_message(message) if len(messages) >= 5: break @@ -311,11 +317,11 @@ def test_queue_by_servicebus_client_iter_messages_simple(self, servicebus_namesp count = 0 for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - message.renew_lock() + receiver.renew_message_lock(message) count += 1 with pytest.raises(StopIteration): @@ -345,10 +351,10 @@ def test_queue_by_servicebus_conn_str_client_iter_messages_with_abandon(self, se print_message(_logger, message) if not message.delivery_count: count += 1 - message.abandon() + receiver.abandon_message(message) else: assert message.delivery_count == 1 - message.complete() + receiver.complete_message(message) assert count == 10 @@ -356,7 +362,7 @@ def test_queue_by_servicebus_conn_str_client_iter_messages_with_abandon(self, se count = 0 for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) count += 1 assert count == 0 @@ -387,14 +393,14 @@ def test_queue_by_servicebus_client_iter_messages_with_defer(self, servicebus_na deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5, receive_mode=ReceiveMode.PeekLock) as receiver: count = 0 for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) count += 1 assert count == 0 @@ -424,18 +430,17 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_client( deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 deferred = receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - message.complete() + receiver.complete_message(message) with pytest.raises(ServiceBusError): receiver.receive_deferred_messages(deferred_messages) - @pytest.mark.liveTest @pytest.mark.live_test_only @@ -461,7 +466,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 @@ -475,8 +480,8 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive assert message.lock_token assert message.locked_until_utc assert message._receiver - message.renew_lock() - message.complete() + receiver.renew_message_lock(message) + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -486,8 +491,6 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receiver_deadletter(self, servicebus_namespace_connection_string, servicebus_queue, **kwargs): with ServiceBusClient.from_connection_string( servicebus_namespace_connection_string, logging_enable=False) as sb_client: - - with sb_client.get_queue_sender(servicebus_queue.name) as sender: deferred_messages = [] for i in range(10): @@ -502,7 +505,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 @@ -512,7 +515,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") count = 0 with sb_client.get_queue_receiver(servicebus_queue.name, @@ -525,7 +528,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - message.complete() + receiver.complete_message(message) assert count == 10 @pytest.mark.liveTest @@ -548,7 +551,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, @@ -559,10 +562,9 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_receive for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) with pytest.raises(ServiceBusError): deferred = receiver.receive_deferred_messages(deferred_messages) - @pytest.mark.liveTest @pytest.mark.live_test_only @@ -588,7 +590,7 @@ def test_queue_by_servicebus_client_iter_messages_with_retrieve_deferred_not_fou deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 3 @@ -624,7 +626,8 @@ def test_queue_by_servicebus_client_receive_batch_with_deadletter(self, serviceb for message in messages: print_message(_logger, message) count += 1 - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", + error_description="Testing description") messages = receiver.receive_messages() assert count == 10 @@ -635,7 +638,7 @@ def test_queue_by_servicebus_client_receive_batch_with_deadletter(self, serviceb count = 0 for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) count += 1 assert count == 0 @@ -646,7 +649,7 @@ def test_queue_by_servicebus_client_receive_batch_with_deadletter(self, serviceb receive_mode=ReceiveMode.PeekLock) as dl_receiver: count = 0 for message in dl_receiver: - message.complete() + dl_receiver.complete_message(message) count += 1 assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description' @@ -679,7 +682,8 @@ def test_queue_by_servicebus_client_receive_batch_with_retrieve_deadletter(self, while messages: for message in messages: print_message(_logger, message) - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", + error_description="Testing description") count += 1 messages = receiver.receive_messages() @@ -699,7 +703,7 @@ def test_queue_by_servicebus_client_receive_batch_with_retrieve_deadletter(self, assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - message.complete() + dl_receiver.complete_message(message) count += 1 assert count == 10 @@ -739,12 +743,11 @@ def test_queue_by_servicebus_client_browse_messages_client(self, servicebus_name with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = receiver.peek_messages(5) assert len(messages) == 5 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() - + with pytest.raises(MessageSettleFailed): + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -779,7 +782,7 @@ def test_queue_by_servicebus_client_browse_messages_with_receiver(self, serviceb messages = receiver.peek_messages(5) assert len(messages) > 0 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) assert b''.join(message.body) == b'Test message' @@ -793,8 +796,8 @@ def test_queue_by_servicebus_client_browse_messages_with_receiver(self, serviceb assert message.to == 'to' assert message.reply_to == 'reply_to' assert message.time_to_live == timedelta(seconds=60) - with pytest.raises(AttributeError): - message.complete() + with pytest.raises(MessageSettleFailed): + receiver.complete_message(message) sender.send_messages(message) @@ -811,7 +814,7 @@ def test_queue_by_servicebus_client_browse_messages_with_receiver(self, serviceb assert message.to == 'to' assert message.reply_to == 'reply_to' assert message.time_to_live == timedelta(seconds=60) - message.complete() + receiver.complete_message(message) cnt += 1 assert cnt == 10 @@ -881,19 +884,19 @@ def test_queue_by_servicebus_client_renew_message_locks(self, servicebus_namespa messages.extend(recv) try: - for m in messages: - assert not m._lock_expired + for message in messages: + assert not message._lock_expired time.sleep(5) - initial_expiry = m.locked_until_utc - m.renew_lock() - assert (m.locked_until_utc - initial_expiry) >= timedelta(seconds=5) + initial_expiry = message.locked_until_utc + receiver.renew_message_lock(message) + assert (message.locked_until_utc - initial_expiry) >= timedelta(seconds=5) finally: - messages[0].complete() - messages[1].complete() + receiver.complete_message(messages[0]) + receiver.complete_message(messages[1]) assert (messages[2].locked_until_utc - utc_now()) <= timedelta(seconds=60) sleep_until_expired(messages[2]) with pytest.raises(MessageLockExpired): - messages[2].complete() + receiver.complete_message(messages[2]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -920,7 +923,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_with_autolockrenew(self, if not messages: messages.append(message) assert not message._lock_expired - renewer.register(message, timeout=60) + renewer.register(receiver, message, timeout=60) print("Registered lock renew thread", message.locked_until_utc, utc_now()) time.sleep(60) print("Finished first sleep", message.locked_until_utc) @@ -930,7 +933,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_with_autolockrenew(self, print("Finished second sleep", message.locked_until_utc, utc_now()) assert message._lock_expired try: - message.complete() + receiver.complete_message(message) raise AssertionError("Didn't raise MessageLockExpired") except MessageLockExpired as e: assert isinstance(e.inner_exception, AutoLockRenewTimeout) @@ -939,12 +942,12 @@ def test_queue_by_queue_client_conn_str_receive_handler_with_autolockrenew(self, print("Remaining messages", message.locked_until_utc, utc_now()) assert message._lock_expired with pytest.raises(MessageLockExpired): - message.complete() + receiver.complete_message(message) else: assert message.delivery_count >= 1 print("Remaining messages", message.locked_until_utc, utc_now()) messages.append(message) - message.complete() + receiver.complete_message(message) renewer.close() assert len(messages) == 11 @@ -978,7 +981,7 @@ def test_queue_message_time_to_live(self, servicebus_namespace_connection_string count = 0 for message in dl_receiver: print_message(_logger, message) - message.complete() + dl_receiver.complete_message(message) count += 1 assert count == 1 @@ -1006,7 +1009,7 @@ def test_queue_message_duplicate_detection(self, servicebus_namespace_connection for message in receiver: print_message(_logger, message) assert message.message_id == message_id - message.complete() + receiver.complete_message(message) count += 1 assert count == 1 @@ -1031,7 +1034,7 @@ def test_queue_message_connection_closed(self, servicebus_namespace_connection_s assert len(messages) == 1 with pytest.raises(MessageSettleFailed): - messages[0].complete() + receiver.complete_message(messages[0]) @pytest.mark.liveTest @@ -1055,16 +1058,16 @@ def test_queue_message_expiry(self, servicebus_namespace_connection_string, serv time.sleep((messages[0].locked_until_utc - utc_now()).total_seconds()+1) assert messages[0]._lock_expired with pytest.raises(MessageLockExpired): - messages[0].complete() + receiver.complete_message(messages[0]) with pytest.raises(MessageLockExpired): - messages[0].renew_lock() + receiver.renew_message_lock(messages[0]) with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = receiver.receive_messages(max_wait_time=30) assert len(messages) == 1 print_message(_logger, messages[0]) assert messages[0].delivery_count > 0 - messages[0].complete() + receiver.complete_message(messages[0]) @pytest.mark.liveTest @@ -1086,12 +1089,12 @@ def test_queue_message_lock_renew(self, servicebus_namespace_connection_string, messages = receiver.receive_messages(max_wait_time=10) assert len(messages) == 1 time.sleep(15) - messages[0].renew_lock() + receiver.renew_message_lock(messages[0]) time.sleep(15) - messages[0].renew_lock() + receiver.renew_message_lock(messages[0]) time.sleep(15) assert not messages[0]._lock_expired - messages[0].complete() + receiver.complete_message(messages[0]) with sb_client.get_queue_receiver(servicebus_queue.name) as receiver: messages = receiver.receive_messages(max_wait_time=10) @@ -1116,18 +1119,18 @@ def test_queue_message_receive_and_delete(self, servicebus_namespace_connection_ receive_mode=ReceiveMode.ReceiveAndDelete) as receiver: messages = receiver.receive_messages(max_wait_time=10) assert len(messages) == 1 - received = messages[0] - print_message(_logger, received) + message = messages[0] + print_message(_logger, message) with pytest.raises(MessageAlreadySettled): - received.complete() + receiver.complete_message(message) with pytest.raises(MessageAlreadySettled): - received.abandon() + receiver.abandon_message(message) with pytest.raises(MessageAlreadySettled): - received.defer() + receiver.defer_message(message) with pytest.raises(MessageAlreadySettled): - received.dead_letter() + receiver.dead_letter_message(message) with pytest.raises(MessageAlreadySettled): - received.renew_lock() + receiver.renew_message_lock(message) time.sleep(30) @@ -1195,7 +1198,7 @@ def message_content(): assert message.enqueued_time_utc assert message.expires_at_utc == (message.enqueued_time_utc + timedelta(seconds=60)) print_message(_logger, message) - message.complete() + receiver.complete_message(message) count += 1 @@ -1229,8 +1232,8 @@ def test_queue_schedule_message(self, servicebus_namespace_connection_string, se assert messages[0].scheduled_enqueue_time_utc <= messages[0].enqueued_time_utc.replace(microsecond=0) assert len(messages) == 1 finally: - for m in messages: - m.complete() + for message in messages: + receiver.complete_message(message) else: raise Exception("Failed to receive schdeduled message.") @@ -1273,7 +1276,7 @@ def test_queue_schedule_multiple_messages(self, servicebus_namespace_connection_ received_messages = [] for message in receiver.get_streaming_message_iter(max_wait_time=5): received_messages.append(message) - message.complete() + receiver.complete_message(message) tokens = sender.schedule_messages(received_messages, scheduled_enqueue_time) assert len(tokens) == 2 @@ -1302,8 +1305,8 @@ def test_queue_schedule_multiple_messages(self, servicebus_namespace_connection_ assert messages[0].message.delivery_tag is not None assert len(messages) == 2 finally: - for m in messages: - m.complete() + for message in messages: + receiver.complete_message(message) else: raise Exception("Failed to receive schdeduled message.") @@ -1332,9 +1335,9 @@ def test_queue_cancel_scheduled_messages(self, servicebus_namespace_connection_s try: assert len(messages) == 0 except AssertionError: - for m in messages: - print(str(m)) - m.complete() + for message in messages: + print(str(message)) + receiver.complete_message(message) raise @@ -1398,8 +1401,7 @@ def test_queue_message_settle_through_mgmt_link_due_to_broken_receiver_link(self messages = receiver.receive_messages(max_wait_time=5) receiver._handler.message_handler.destroy() # destroy the underlying receiver link assert len(messages) == 1 - messages[0].complete() - + receiver.complete_message(messages[0]) def test_queue_mock_auto_lock_renew_callback(self): results = [] @@ -1409,11 +1411,12 @@ def callback_mock(renewable, error): if error: errors.append(error) + receiver = MockReceiver() auto_lock_renew = AutoLockRenewer() auto_lock_renew._renew_period = 1 # So we can run the test fast. with auto_lock_renew: # Check that it is called when the object expires for any reason (silent renew failure) message = MockReceivedMessage(prevent_renew_lock=True) - auto_lock_renew.register(renewable=message, on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=message, on_lock_renew_failure=callback_mock) time.sleep(3) assert len(results) == 1 and results[-1]._lock_expired == True assert not errors @@ -1423,7 +1426,7 @@ def callback_mock(renewable, error): auto_lock_renew = AutoLockRenewer() auto_lock_renew._renew_period = 1 with auto_lock_renew: # Check that in normal operation it does not get called - auto_lock_renew.register(renewable=MockReceivedMessage(), on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=MockReceivedMessage(), on_lock_renew_failure=callback_mock) time.sleep(3) assert not results assert not errors @@ -1434,7 +1437,7 @@ def callback_mock(renewable, error): auto_lock_renew._renew_period = 1 with auto_lock_renew: # Check that when a message is settled, it will not get called even after expiry message = MockReceivedMessage(prevent_renew_lock=True) - auto_lock_renew.register(renewable=message, on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=message, on_lock_renew_failure=callback_mock) message._settled = True time.sleep(3) assert not results @@ -1446,7 +1449,7 @@ def callback_mock(renewable, error): auto_lock_renew._renew_period = 1 with auto_lock_renew: # Check that it is called when there is an overt renew failure message = MockReceivedMessage(exception_on_renew_lock=True) - auto_lock_renew.register(renewable=message, on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=message, on_lock_renew_failure=callback_mock) time.sleep(3) assert len(results) == 1 and results[-1]._lock_expired == True assert errors[-1] @@ -1457,7 +1460,7 @@ def callback_mock(renewable, error): auto_lock_renew._renew_period = 1 with auto_lock_renew: # Check that it is not called when the renewer is shutdown message = MockReceivedMessage(prevent_renew_lock=True) - auto_lock_renew.register(renewable=message, on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=message, on_lock_renew_failure=callback_mock) auto_lock_renew.close() time.sleep(3) assert not results @@ -1469,18 +1472,19 @@ def callback_mock(renewable, error): auto_lock_renew._renew_period = 1 with auto_lock_renew: # Check that it is not called when the receiver is shutdown message = MockReceivedMessage(prevent_renew_lock=True) - auto_lock_renew.register(renewable=message, on_lock_renew_failure=callback_mock) + auto_lock_renew.register(receiver, renewable=message, on_lock_renew_failure=callback_mock) message._receiver._running = False time.sleep(3) assert not results assert not errors - def test_queue_mock_no_reusing_auto_lock_renew(self): + + receiver = MockReceiver() auto_lock_renew = AutoLockRenewer() auto_lock_renew._renew_period = 1 # So we can run the test fast. with auto_lock_renew: - auto_lock_renew.register(renewable=MockReceivedMessage()) + auto_lock_renew.register(receiver, renewable=MockReceivedMessage()) time.sleep(3) with pytest.raises(ServiceBusError): @@ -1488,13 +1492,13 @@ def test_queue_mock_no_reusing_auto_lock_renew(self): pass with pytest.raises(ServiceBusError): - auto_lock_renew.register(renewable=MockReceivedMessage()) + auto_lock_renew.register(receiver, renewable=MockReceivedMessage()) auto_lock_renew = AutoLockRenewer() auto_lock_renew._renew_period = 1 with auto_lock_renew: - auto_lock_renew.register(renewable=MockReceivedMessage()) + auto_lock_renew.register(receiver, renewable=MockReceivedMessage()) time.sleep(3) auto_lock_renew.close() @@ -1504,7 +1508,7 @@ def test_queue_mock_no_reusing_auto_lock_renew(self): pass with pytest.raises(ServiceBusError): - auto_lock_renew.register(renewable=MockReceivedMessage()) + auto_lock_renew.register(receiver, renewable=MockReceivedMessage()) def test_queue_message_properties(self): scheduled_enqueue_time = (utc_now() + timedelta(seconds=20)).replace(microsecond=0) @@ -1650,12 +1654,12 @@ def message_content(): if message.label == '1st': message_1st_received_cnt += 1 - message.complete() + receiver.complete_message(message) message.label = '2nd' sender.send_messages(message) # resending received message elif message.label == '2nd': message_2nd_received_cnt += 1 - message.complete() + receiver.complete_message(message) assert message_1st_received_cnt == 20 and message_2nd_received_cnt == 20 # Network/server might be unstable making flow control ineffective in the leading rounds of connection iteration @@ -1686,8 +1690,8 @@ def test_queue_receiver_alive_after_timeout(self, servicebus_namespace_connectio for message in receiver.get_streaming_message_iter(): messages.append(message) - for m in messages: - m.complete() + for message in messages: + receiver.complete_message(message) assert len(messages) == 2 assert str(messages[0]) == "0" @@ -1706,8 +1710,8 @@ def test_queue_receiver_alive_after_timeout(self, servicebus_namespace_connectio assert str(messages[2]) == "2" assert str(messages[3]) == "3" - for m in messages[2:]: - m.complete() + for message in messages[2:]: + receiver.complete_message(message) messages = receiver.receive_messages() assert not messages @@ -1734,9 +1738,9 @@ def test_queue_receive_keep_conn_alive(self, servicebus_namespace_connection_str receiver_handler = receiver._handler assert len(messages) == 2 time.sleep(4 * 60 + 5) # 240s is the service defined connection idle timeout - messages[0].renew_lock() # check mgmt link operation - messages[0].complete() - messages[1].complete() # check receiver link operation + receiver.renew_message_lock(messages[0]) # check mgmt link operation + receiver.complete_message(messages[0]) + receiver.complete_message(messages[1]) # check receiver link operation time.sleep(60) # sleep another one minute to ensure we pass the lock_duration time @@ -1794,7 +1798,7 @@ def test_queue_receiver_respects_max_wait_time_overrides(self, servicebus_namesp time_1 = receiver._handler._counter.get_current_ms() for message in receiver.get_streaming_message_iter(max_wait_time=10): messages.append(message) - message.complete() + receiver.complete_message(message) time_2 = receiver._handler._counter.get_current_ms() for message in receiver.get_streaming_message_iter(max_wait_time=1): @@ -1993,7 +1997,7 @@ def _hack_amqp_message_complete(cls): def _hack_amqp_mgmt_request(cls, message, operation, op_type=None, node=None, callback=None, **kwargs): raise uamqp.errors.AMQPConnectionError() - def _hack_sb_message_settle_message(self, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): + def _hack_sb_receiver_settle_message(self, message, settle_operation, dead_letter_reason=None, dead_letter_error_description=None): raise uamqp.errors.AMQPError() with ServiceBusClient.from_connection_string( @@ -2005,7 +2009,7 @@ def _hack_sb_message_settle_message(self, settle_operation, dead_letter_reason=N sender.send_messages(ServiceBusMessage("body"), timeout=5) message = receiver.receive_messages()[0] message.message.accept = types.MethodType(_hack_amqp_message_complete, message.message) - message.complete() # settle via mgmt link + receiver.complete_message(message) # settle via mgmt link try: origin_amqp_mgmt_request_method = receiver._handler.mgmt_request @@ -2018,12 +2022,15 @@ def _hack_sb_message_settle_message(self, settle_operation, dead_letter_reason=N sender.send_messages(ServiceBusMessage("body"), timeout=5) message = receiver.receive_messages()[0] - message._settle_message = types.MethodType(_hack_sb_message_settle_message, message) + + origin_sb_receiver_settle_message_method = receiver._settle_message + receiver._settle_message = types.MethodType(_hack_sb_receiver_settle_message, receiver) with pytest.raises(MessageSettleFailed): - message.complete() + receiver.complete_message(message) + receiver._settle_message = origin_sb_receiver_settle_message_method message = receiver.receive_messages(max_wait_time=6)[0] - message.complete() + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -2041,4 +2048,4 @@ def test_send_message_no_body(self, servicebus_namespace_connection_string, serv max_wait_time=10) as receiver: message = receiver.next() assert message.body is None - message.complete() + receiver.complete_message(message) diff --git a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py index e5214b52a864..f40a76e9d8e8 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py +++ b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py @@ -15,7 +15,7 @@ from azure.mgmt.servicebus.models import AccessRights from azure.servicebus import ServiceBusClient, ServiceBusSender from azure.servicebus._base_handler import ServiceBusSharedKeyCredential -from azure.servicebus._common.message import ServiceBusMessage, ServiceBusPeekedMessage +from azure.servicebus._common.message import ServiceBusMessage, ServiceBusReceivedMessage from azure.servicebus.exceptions import ( ServiceBusError, ServiceBusConnectionError, diff --git a/sdk/servicebus/azure-servicebus/tests/test_sessions.py b/sdk/servicebus/azure-servicebus/tests/test_sessions.py index 460c309a6b52..2d2828a665fa 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_sessions.py +++ b/sdk/servicebus/azure-servicebus/tests/test_sessions.py @@ -11,13 +11,12 @@ import pytest import time import uuid -from datetime import datetime, timedelta +from datetime import timedelta from azure.servicebus import ( ServiceBusClient, AutoLockRenewer, ServiceBusMessage, - ServiceBusPeekedMessage, ServiceBusReceivedMessage, ReceiveMode, NEXT_AVAILABLE_SESSION, @@ -60,9 +59,9 @@ def test_session_by_session_client_conn_str_receive_handler_peeklock(self, servi session_id = str(uuid.uuid4()) sender = sb_client.get_queue_sender(servicebus_queue.name) - session = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) + receiver = sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) - with sender, session: + with sender, receiver: for i in range(3): message = ServiceBusMessage("Handler message no. {}".format(i)) message.session_id = session_id @@ -78,11 +77,11 @@ def test_session_by_session_client_conn_str_receive_handler_peeklock(self, servi sender.send_messages(message) with pytest.raises(ServiceBusConnectionError): - session = sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5)._open_with_retry() + receiver = sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5)._open_with_retry() count = 0 received_cnt_dic = {} - for message in session: + for message in receiver: print_message(_logger, message) assert message.delivery_count == 0 assert message.properties @@ -99,7 +98,7 @@ def test_session_by_session_client_conn_str_receive_handler_peeklock(self, servi assert message.session_id == session_id assert message.reply_to_session_id == 'reply_to_session_id' count += 1 - message.complete() + receiver.complete_message(message) if message.message_id not in received_cnt_dic: received_cnt_dic[message.message_id] = 1 sender.send_messages(message) @@ -128,15 +127,15 @@ def test_session_by_queue_client_conn_str_receive_handler_receiveanddelete(self, with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, receive_mode=ReceiveMode.ReceiveAndDelete, - max_wait_time=5) as session: - for message in session: + max_wait_time=5) as receiver: + for message in receiver: messages.append(message) - assert session_id == session._session_id + assert session_id == receiver._session_id assert session_id == message.session_id with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) - assert not session._running + assert not receiver._running assert len(messages) == 10 time.sleep(30) @@ -162,28 +161,28 @@ def test_session_by_session_client_conn_str_receive_handler_with_stop(self, serv sender.send_messages(message) messages = [] - with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - for message in session: - assert session_id == session.session.session_id + with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + for message in receiver: + assert session_id == receiver.session.session_id assert session_id == message.session_id messages.append(message) - message.complete() + receiver.complete_message(message) if len(messages) >= 5: break - assert session._running + assert receiver._running assert len(messages) == 5 - with session: - for message in session: - assert session_id == session.session.session_id + with receiver: + for message in receiver: + assert session_id == receiver.session.session_id assert session_id == message.session_id messages.append(message) - message.complete() + receiver.complete_message(message) if len(messages) >= 5: break - assert not session._running + assert not receiver._running assert len(messages) == 6 @pytest.mark.liveTest @@ -279,19 +278,19 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_recei count = 0 with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, - max_wait_time=5) as session: - for message in session: + max_wait_time=5) as receiver: + for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, - max_wait_time=5) as session: - deferred = session.receive_deferred_messages(deferred_messages) + max_wait_time=5) as receiver: + deferred = receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) @@ -299,8 +298,8 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_recei assert not message.locked_until_utc assert message._receiver with pytest.raises(TypeError): - message.renew_lock() - message.complete() + receiver.renew_message_lock(message) + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -320,27 +319,28 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_recei count = 0 with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, - max_wait_time=5) as session: - for message in session: + max_wait_time=5) as receiver: + for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, - max_wait_time=5) as session: - deferred = session.receive_deferred_messages(deferred_messages) + max_wait_time=5) as receiver: + deferred = receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", + error_description="Testing description") count = 0 with sb_client.get_queue_receiver(servicebus_queue.name, - sub_queue = SubQueue.DeadLetter, + sub_queue=SubQueue.DeadLetter, max_wait_time=5) as receiver: for message in receiver: count += 1 @@ -349,7 +349,7 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_recei assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' assert message.properties[b'DeadLetterErrorDescription'] == b'Testing description' - message.complete() + receiver.complete_message(message) assert count == 10 @pytest.mark.liveTest @@ -369,27 +369,26 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_recei sender.send_messages(message) count = 0 - with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: - for message in session: + with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: + for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, - receive_mode=ReceiveMode.ReceiveAndDelete) as session: - deferred = session.receive_deferred_messages(deferred_messages) + receive_mode=ReceiveMode.ReceiveAndDelete) as receiver: + deferred = receiver.receive_deferred_messages(deferred_messages) assert len(deferred) == 10 for message in deferred: assert isinstance(message, ServiceBusReceivedMessage) with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) with pytest.raises(ServiceBusError): - deferred = session.receive_deferred_messages(deferred_messages) - + deferred = receiver.receive_deferred_messages(deferred_messages) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -407,20 +406,20 @@ def test_session_by_servicebus_client_iter_messages_with_retrieve_deferred_clien message = ServiceBusMessage("Deferred message no. {}".format(i), session_id=session_id) sender.send_messages(message) - with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as session: + with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5) as receiver: count = 0 - for message in session: + for message in receiver: deferred_messages.append(message.sequence_number) print_message(_logger, message) count += 1 - message.defer() + receiver.defer_message(message) assert count == 10 - deferred = session.receive_deferred_messages(deferred_messages) + deferred = receiver.receive_deferred_messages(deferred_messages) with pytest.raises(MessageAlreadySettled): - message.complete() + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -448,18 +447,18 @@ def test_session_by_servicebus_client_receive_with_retrieve_deadletter(self, ser while messages: for message in messages: print_message(_logger, message) - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") count += 1 messages = receiver.receive_messages() assert count == 10 with sb_client.get_queue_receiver(servicebus_queue.name, sub_queue = SubQueue.DeadLetter, - max_wait_time=5) as session: + max_wait_time=5) as receiver: count = 0 - for message in session: + for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description' assert message.properties[b'DeadLetterReason'] == b'Testing reason' @@ -493,11 +492,11 @@ def test_session_by_servicebus_client_browse_messages_client(self, servicebus_na with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id) as receiver: messages = receiver.peek_messages(5) assert len(messages) == 5 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() + with pytest.raises(MessageSettleFailed): + receiver.complete_message(message) with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id_2) as receiver: messages = receiver.peek_messages(5) @@ -523,12 +522,11 @@ def test_session_by_servicebus_client_browse_messages_with_receiver(self, servic messages = receiver.peek_messages(5) assert len(messages) > 0 - assert all(isinstance(m, ServiceBusPeekedMessage) for m in messages) + assert all(isinstance(m, ServiceBusReceivedMessage) for m in messages) for message in messages: print_message(_logger, message) - with pytest.raises(AttributeError): - message.complete() - + with pytest.raises(MessageSettleFailed): + receiver.complete_message(message) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -565,8 +563,8 @@ def test_session_by_servicebus_client_renew_client_locks(self, servicebus_namesp receiver.session.renew_lock(timeout=5) assert (receiver.session._locked_until_utc - initial_expiry) >= timedelta(seconds=5) finally: - messages[0].complete() - messages[1].complete() + receiver.complete_message(messages[0]) + receiver.complete_message(messages[1]) # This magic number is because of a 30 second lock renewal window. Chose 31 seconds because at 30, you'll see "off by .05 seconds" flaky failures # potentially as a side effect of network delays/sleeps/"typical distributed systems nonsense." In a perfect world we wouldn't have a magic number/network hop but this allows @@ -574,8 +572,7 @@ def test_session_by_servicebus_client_renew_client_locks(self, servicebus_namesp assert (receiver.session._locked_until_utc - utc_now()) <= timedelta(seconds=60) sleep_until_expired(receiver.session) with pytest.raises(SessionLockExpired): - messages[2].complete() - + receiver.complete_message(messages[2]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -600,7 +597,7 @@ def lock_lost_callback(renewable, error): renewer = AutoLockRenewer() messages = [] with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.PeekLock, prefetch_count=10) as receiver: - renewer.register(receiver.session, timeout=60, on_lock_renew_failure = lock_lost_callback) + renewer.register(receiver, receiver.session, timeout=60, on_lock_renew_failure = lock_lost_callback) print("Registered lock renew thread", receiver.session._locked_until_utc, utc_now()) with pytest.raises(SessionLockExpired): for message in receiver: @@ -613,9 +610,9 @@ def lock_lost_callback(renewable, error): message._lock_expired assert message.locked_until_utc is None with pytest.raises(TypeError): - message.renew_lock() + receiver.renew_message_lock(message) assert message.lock_token is not None - message.complete() + receiver.complete_message(message) messages.append(message) elif len(messages) == 1: @@ -627,7 +624,7 @@ def lock_lost_callback(renewable, error): assert receiver.session._lock_expired assert isinstance(receiver.session.auto_renew_error, AutoLockRenewTimeout) try: - message.complete() + receiver.complete_message(message) raise AssertionError("Didn't raise SessionLockExpired") except SessionLockExpired as e: assert isinstance(e.inner_exception, AutoLockRenewTimeout) @@ -639,7 +636,7 @@ def lock_lost_callback(renewable, error): with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, max_wait_time=5, receive_mode=ReceiveMode.PeekLock, prefetch_count=10) as receiver: session = receiver.session - renewer.register(session, timeout=5, on_lock_renew_failure=lock_lost_callback) + renewer.register(receiver, session, timeout=5, on_lock_renew_failure=lock_lost_callback) sleep_until_expired(receiver.session) assert not results @@ -669,8 +666,7 @@ def test_session_message_connection_closed(self, servicebus_namespace_connection assert len(messages) == 1 with pytest.raises(MessageSettleFailed): - messages[0].complete() - + receiver.complete_message(messages[0]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -696,11 +692,11 @@ def test_session_message_expiry(self, servicebus_namespace_connection_string, se with pytest.raises(TypeError): messages[0]._lock_expired with pytest.raises(TypeError): - messages[0].renew_lock() + receiver.renew_message_lock(messages[0]) #TODO: Bug: Why was this 30s sleep before? compare with T1. assert receiver.session._lock_expired with pytest.raises(SessionLockExpired): - messages[0].complete() + receiver.complete_message(messages[0]) with pytest.raises(SessionLockExpired): receiver.session.renew_lock() @@ -709,8 +705,7 @@ def test_session_message_expiry(self, servicebus_namespace_connection_string, se assert len(messages) == 1 print_message(_logger, messages[0]) assert messages[0].delivery_count - messages[0].complete() - + receiver.complete_message(messages[0]) @pytest.mark.liveTest @pytest.mark.live_test_only @@ -921,11 +916,11 @@ def test_session_by_servicebus_client_session_pool(self, servicebus_namespace_co def message_processing(sb_client): while True: try: - with sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) as session: - for message in session: - print("ServiceBusMessage: {}".format(message)) + with sb_client.get_queue_receiver(servicebus_queue.name, session_id=NEXT_AVAILABLE_SESSION, max_wait_time=5) as receiver: + for message in receiver: + print("ServiceBusReceivedMessage: {}".format(message)) messages.append(message) - message.complete() + receiver.complete_message(message) except NoActiveSession: return except Exception as e: @@ -970,7 +965,7 @@ def test_session_by_session_client_conn_str_receive_handler_peeklock_abandon(sel with sb_client.get_queue_receiver(servicebus_queue.name, session_id=session_id, prefetch_count=0, max_wait_time=5) as receiver: message = receiver.next() assert message.sequence_number == 1 - message.abandon() + receiver.abandon_message(message) for next_message in receiver: # we can't be sure there won't be a service delay, so we may not get the message back _immediately_, even if in most cases it shows right back up. if not next_message: raise Exception("Did not successfully re-receive abandoned message, sequence_number 1 was not observed.") @@ -1001,10 +996,9 @@ def test_session_basic_topic_subscription_send_and_receive(self, servicebus_name count = 0 for message in receiver: count += 1 - message.complete() + receiver.complete_message(message) assert count == 1 - @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') diff --git a/sdk/servicebus/azure-servicebus/tests/test_subscriptions.py b/sdk/servicebus/azure-servicebus/tests/test_subscriptions.py index ebc690e35d58..0a6ecc63ff57 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_subscriptions.py +++ b/sdk/servicebus/azure-servicebus/tests/test_subscriptions.py @@ -54,7 +54,7 @@ def test_subscription_by_subscription_client_conn_str_receive_basic(self, servic count = 0 for message in receiver: count += 1 - message.complete() + receiver.complete_message(message) assert count == 1 @pytest.mark.liveTest @@ -86,7 +86,7 @@ def test_subscription_by_sas_token_credential_conn_str_send_basic(self, serviceb count = 0 for message in receiver: count += 1 - message.complete() + receiver.complete_message(message) assert count == 1 @pytest.mark.skip(reason="Pending management apis") @@ -142,7 +142,7 @@ def test_subscription_by_servicebus_client_receive_batch_with_deadletter(self, s for message in messages: print_message(_logger, message) count += 1 - message.dead_letter(reason="Testing reason", error_description="Testing description") + receiver.dead_letter_message(message, reason="Testing reason", error_description="Testing description") messages = receiver.receive_messages() assert count == 10 @@ -156,7 +156,7 @@ def test_subscription_by_servicebus_client_receive_batch_with_deadletter(self, s count = 0 for message in receiver: print_message(_logger, message) - message.complete() + receiver.complete_message(message) count += 1 assert count == 0 @@ -169,7 +169,7 @@ def test_subscription_by_servicebus_client_receive_batch_with_deadletter(self, s ) as dl_receiver: count = 0 for message in dl_receiver: - message.complete() + dl_receiver.complete_message(message) count += 1 assert message.dead_letter_reason == 'Testing reason' assert message.dead_letter_error_description == 'Testing description'