Skip to content

Commit ea9f225

Browse files
author
Natan Tolparov
committed
feat(retries): added throttling mode
1 parent 30ae0b3 commit ea9f225

File tree

5 files changed

+28
-16
lines changed

5 files changed

+28
-16
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,22 @@ set_up_yc_api_endpoint(kz_region_endpoint)
157157
```
158158

159159
### Retries
160-
If you want to retry SDK requests, build SDK with `retry_policy` field using `RetryPolicy` class:
160+
SDK provide built-in retry policy, that supports [exponential backoff and jitter](https://aws.amazon.com/ru/blogs/architecture/exponential-backoff-and-jitter/), and also [retry budget](https://github.com/grpc/proposal/blob/master/A6-client-retries.md#throttling-retry-attempts-and-hedged-rpcs). It's necessary to avoid retry amplification.
161161

162162
```python
163163
import grpc
164164
from yandexcloud import SDK, RetryPolicy
165165

166-
sdk = SDK(retry_policy=RetryPolicy(max_attempts=2, status_codes=(grpc.StatusCode.UNAVAILABLE,)))
166+
sdk = SDK(retry_policy=RetryPolicy())
167167
```
168168

169-
It's **strongly recommended** to use default settings of RetryPolicy to avoid retry amplification:
170-
```python
171-
import grpc
172-
from yandexcloud import SDK, RetryPolicy
169+
SDK provide different modes for retry throttling policy:
170+
171+
* `persistent` is suitable when you use SDK in any long-lived application, when SDK instance will live long enough for manage budget;
172+
* `temporary` is suitable when you use SDK in any short-lived application, e.g. scripts or CI/CD.
173+
174+
By default, SDK will use temporary mode, but you can change it through `throttling_mode` argument.
173175

174-
sdk = SDK(retry_policy=RetryPolicy())
175-
```
176176

177177
## Contributing
178178
### Dependencies

tests/test_retry_policy.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
NetworkServiceStub,
1111
add_NetworkServiceServicer_to_server,
1212
)
13-
from yandexcloud import SDK, RetryPolicy
13+
from yandexcloud import SDK, RetryPolicy, ThrottlingMode
1414
from yandexcloud._channels import Channels
1515

1616
INSECURE_SERVICE_PORT = "50051"
@@ -27,7 +27,7 @@ def side_effect_unavailable(_, context):
2727

2828
class VPCServiceMock:
2929
def __init__(self, fn):
30-
self.Get = Mock(return_value=Network(id="12342314"))
30+
self.Get = Mock(side_effect=fn)
3131
self.Create = Mock()
3232
self.Update = Mock()
3333
self.Delete = Mock()
@@ -65,7 +65,7 @@ def test_default_retries(mock_channel):
6565
server, service = grpc_server(side_effect_unavailable)
6666

6767
sdk = SDK(
68-
retry_policy=RetryPolicy(),
68+
retry_policy=RetryPolicy(throttling_mode=ThrottlingMode.PERSISTENT),
6969
endpoint=f"localhost:{INSECURE_SERVICE_PORT}",
7070
endpoints={
7171
"vpc": SERVICE_ADDR + ":" + INSECURE_SERVICE_PORT,
@@ -98,7 +98,8 @@ def test_custom_retries(mock_channel):
9898
request = GetNetworkRequest(network_id="asdf")
9999
network_client.Get(request)
100100
except grpc.RpcError:
101-
assert service.Get.call_count == 4
101+
# because of temporary throttling mode by default
102+
assert service.Get.call_count == 3
102103

103104
server.stop(0)
104105

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

yandexcloud/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
default_backoff,
99
)
1010
from yandexcloud._retry_interceptor import RetryInterceptor
11-
from yandexcloud._retry_policy import RetryPolicy
11+
from yandexcloud._retry_policy import RetryPolicy, ThrottlingMode
1212
from yandexcloud._sdk import SDK
1313

1414
__version__ = "0.336.0"

yandexcloud/_retry_policy.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import json
2+
from enum import Enum
23
from typing import Tuple
34

45
import grpc
56

67

8+
class ThrottlingMode(Enum):
9+
PERSISTENT = "persistent"
10+
TEMPORARY = "temporary"
11+
12+
713
class RetryPolicy:
814
def __init__(
915
self,
1016
max_attempts: int = 4,
1117
status_codes: Tuple["grpc.StatusCode"] = (grpc.StatusCode.UNAVAILABLE,),
18+
throttling_mode: ThrottlingMode = ThrottlingMode.TEMPORARY,
1219
):
1320
self.__policy = {
1421
"methodConfig": [
@@ -21,10 +28,14 @@ def __init__(
2128
"backoffMultiplier": 2,
2229
"retryableStatusCodes": [status.name for status in status_codes],
2330
},
31+
"waitForReady": True,
2432
}
2533
],
26-
"retryThrottling": {"maxTokens": 100, "tokenRatio": 0.1},
27-
"waitForReady": True,
34+
"retryThrottling": (
35+
{"maxTokens": 100, "tokenRatio": 0.1}
36+
if throttling_mode == ThrottlingMode.PERSISTENT
37+
else {"maxTokens": 6, "tokenRatio": 0.1}
38+
),
2839
}
2940

3041
def to_json(self) -> str:

0 commit comments

Comments
 (0)