Skip to content

Commit f68dd95

Browse files
authored
[ServiceBus] fixed retry backoff (Azure#22317)
* inital fixed retry commit * changelog + version * pylint
1 parent bc99c22 commit f68dd95

File tree

13 files changed

+143
-8
lines changed

13 files changed

+143
-8
lines changed

sdk/eventhub/azure-eventhub/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release History
22

3-
## 5.6.2 (Unreleased)
3+
## 5.7.0 (Unreleased)
44

55
### Features Added
66

sdk/eventhub/azure-eventhub/azure/eventhub/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55

6-
VERSION = "5.6.2"
6+
VERSION = "5.7.0"

sdk/servicebus/azure-servicebus/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Release History
22

3-
## 7.4.1 (Unreleased)
3+
## 7.5.0 (Unreleased)
44

55
### Features Added
66

7+
- Added support for fixed (linear) retry backoff:
8+
- Sync/async `ServiceBusClient` constructors and `from_connection_string` take `retry_mode` as a keyword argument.
9+
- `RetryMode` enum has been added to `azure.servicebus`, with values `FIXED` and `EXPONENTIAL`.
10+
711
### Breaking Changes
812

913
### Bugs Fixed

sdk/servicebus/azure-servicebus/azure/servicebus/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
parse_connection_string,
2929
ServiceBusConnectionStringProperties,
3030
)
31+
from ._retry import RetryMode
3132

3233
TransportType = constants.TransportType
3334

@@ -46,4 +47,5 @@
4647
"AutoLockRenewer",
4748
"parse_connection_string",
4849
"ServiceBusConnectionStringProperties",
50+
"RetryMode",
4951
]

sdk/servicebus/azure-servicebus/azure/servicebus/_base_handler.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from azure.core.credentials import AccessToken, AzureSasCredential, AzureNamedKeyCredential
2323

2424
from ._common._configuration import Configuration
25+
from ._retry import RetryMode
2526
from .exceptions import (
2627
ServiceBusError,
2728
ServiceBusConnectionError,
@@ -152,6 +153,12 @@ def _generate_sas_token(uri, policy, key, expiry=None):
152153
token = utils.create_sas_token(encoded_policy, encoded_key, encoded_uri, expiry)
153154
return AccessToken(token=token, expires_on=abs_expiry)
154155

156+
def _get_backoff_time(retry_mode, backoff_factor, backoff_max, retried_times):
157+
if retry_mode == RetryMode.FIXED:
158+
backoff_value = backoff_factor
159+
else:
160+
backoff_value = backoff_factor * (2 ** retried_times)
161+
return min(backoff_max, backoff_value)
155162

156163
class ServiceBusSASTokenCredential(object):
157164
"""The shared access token credential used for authentication.
@@ -418,7 +425,12 @@ def _backoff(
418425
):
419426
# type: (int, Exception, Optional[float], str) -> None
420427
entity_name = entity_name or self._container_id
421-
backoff = self._config.retry_backoff_factor * 2 ** retried_times
428+
backoff = _get_backoff_time(
429+
self._config.retry_mode,
430+
self._config.retry_backoff_factor,
431+
self._config.retry_backoff_max,
432+
retried_times,
433+
)
422434
if backoff <= self._config.retry_backoff_max and (
423435
abs_timeout_time is None or (backoff + time.time()) <= abs_timeout_time
424436
): # pylint:disable=no-else-return

sdk/servicebus/azure-servicebus/azure/servicebus/_common/_configuration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
from typing import Optional, Dict, Any
66

77
from uamqp.constants import TransportType
8+
from .._retry import RetryMode
89

910

1011
class Configuration(object): # pylint:disable=too-many-instance-attributes
1112
def __init__(self, **kwargs):
1213
self.user_agent = kwargs.get("user_agent") # type: Optional[str]
1314
self.retry_total = kwargs.get("retry_total", 3) # type: int
15+
self.retry_mode = kwargs.get("retry_mode", RetryMode.EXPONENTIAL)
1416
self.retry_backoff_factor = kwargs.get(
1517
"retry_backoff_factor", 0.8
1618
) # type: float
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
from enum import Enum
6+
7+
class RetryMode(str, Enum):
8+
EXPONENTIAL = 'exponential'
9+
FIXED = 'fixed'

sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class ServiceBusClient(object):
6060
:keyword float retry_backoff_factor: Delta back-off internal in the unit of second between retries.
6161
Default value is 0.8.
6262
:keyword float retry_backoff_max: Maximum back-off interval in the unit of second. Default value is 120.
63+
:keyword retry_mode: Fixed or exponential delay between attempts, default is exponential.
64+
:paramtype retry_mode: ~azure.servicebus.RetryMode
6365
6466
.. admonition:: Example:
6567
@@ -152,6 +154,8 @@ def from_connection_string(cls, conn_str, **kwargs):
152154
:keyword float retry_backoff_factor: Delta back-off internal in the unit of second between retries.
153155
Default value is 0.8.
154156
:keyword float retry_backoff_max: Maximum back-off interval in the unit of second. Default value is 120.
157+
:keyword retry_mode: Fixed or exponential delay between attempts, default is exponential.
158+
:paramtype retry_mode: ~azure.servicebus.RetryMode
155159
:rtype: ~azure.servicebus.ServiceBusClient
156160
157161
.. admonition:: Example:
@@ -212,6 +216,7 @@ def get_queue_sender(self, queue_name, **kwargs):
212216
http_proxy=self._config.http_proxy,
213217
connection=self._connection,
214218
user_agent=self._config.user_agent,
219+
retry_mode=self._config.retry_mode,
215220
retry_total=self._config.retry_total,
216221
retry_backoff_factor=self._config.retry_backoff_factor,
217222
retry_backoff_max=self._config.retry_backoff_max,
@@ -303,6 +308,7 @@ def get_queue_receiver(self, queue_name, **kwargs):
303308
http_proxy=self._config.http_proxy,
304309
connection=self._connection,
305310
user_agent=self._config.user_agent,
311+
retry_mode=self._config.retry_mode,
306312
retry_total=self._config.retry_total,
307313
retry_backoff_factor=self._config.retry_backoff_factor,
308314
retry_backoff_max=self._config.retry_backoff_max,
@@ -344,6 +350,7 @@ def get_topic_sender(self, topic_name, **kwargs):
344350
http_proxy=self._config.http_proxy,
345351
connection=self._connection,
346352
user_agent=self._config.user_agent,
353+
retry_mode=self._config.retry_mode,
347354
retry_total=self._config.retry_total,
348355
retry_backoff_factor=self._config.retry_backoff_factor,
349356
retry_backoff_max=self._config.retry_backoff_max,
@@ -433,6 +440,7 @@ def get_subscription_receiver(self, topic_name, subscription_name, **kwargs):
433440
http_proxy=self._config.http_proxy,
434441
connection=self._connection,
435442
user_agent=self._config.user_agent,
443+
retry_mode=self._config.retry_mode,
436444
retry_total=self._config.retry_total,
437445
retry_backoff_factor=self._config.retry_backoff_factor,
438446
retry_backoff_max=self._config.retry_backoff_max,
@@ -453,6 +461,7 @@ def get_subscription_receiver(self, topic_name, subscription_name, **kwargs):
453461
http_proxy=self._config.http_proxy,
454462
connection=self._connection,
455463
user_agent=self._config.user_agent,
464+
retry_mode=self._config.retry_mode,
456465
retry_total=self._config.retry_total,
457466
retry_backoff_factor=self._config.retry_backoff_factor,
458467
retry_backoff_max=self._config.retry_backoff_max,

sdk/servicebus/azure-servicebus/azure/servicebus/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55

6-
VERSION = "7.4.1"
6+
VERSION = "7.5.0"

sdk/servicebus/azure-servicebus/azure/servicebus/aio/_base_handler_async.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from azure.core.credentials import AccessToken, AzureSasCredential, AzureNamedKeyCredential
1616

17-
from .._base_handler import _generate_sas_token, BaseHandler as BaseHandlerSync
17+
from .._base_handler import _generate_sas_token, BaseHandler as BaseHandlerSync, _get_backoff_time
1818
from .._common._configuration import Configuration
1919
from .._common.utils import create_properties, strip_protocol_from_uri, parse_sas_credential
2020
from .._common.constants import (
@@ -268,7 +268,12 @@ async def _backoff(
268268
self, retried_times, last_exception, abs_timeout_time=None, entity_name=None
269269
):
270270
entity_name = entity_name or self._container_id
271-
backoff = self._config.retry_backoff_factor * 2 ** retried_times
271+
backoff = _get_backoff_time(
272+
self._config.retry_mode,
273+
self._config.retry_backoff_factor,
274+
self._config.retry_backoff_max,
275+
retried_times,
276+
)
272277
if backoff <= self._config.retry_backoff_max and (
273278
abs_timeout_time is None or (backoff + time.time()) <= abs_timeout_time
274279
):

0 commit comments

Comments
 (0)