Skip to content

Commit 5b4dac1

Browse files
nathanKithNatan Tolparov
andauthored
feat(retries): added throttling mode (#139)
* feat(retries): added throttling mode * feat(retries): after review * feat(retries): after review more --------- Co-authored-by: Natan Tolparov <[email protected]>
1 parent 9417d6e commit 5b4dac1

File tree

5 files changed

+33
-18
lines changed

5 files changed

+33
-18
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: 6 additions & 5 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()
@@ -61,11 +61,11 @@ def grpc_server(side_effect):
6161
return server, service
6262

6363

64-
def test_default_retries(mock_channel):
64+
def test_persistent_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.337.0"

yandexcloud/_retry_policy.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
import json
2-
from typing import Tuple
2+
from enum import Enum
3+
from typing import Any, Dict, Tuple
34

45
import grpc
56

67

8+
class ThrottlingMode(str, Enum):
9+
PERSISTENT = "persistent"
10+
TEMPORARY = "temporary"
11+
12+
13+
def get_throttling_policy(throttling_mode: ThrottlingMode) -> Dict[str, Any]:
14+
if throttling_mode == ThrottlingMode.PERSISTENT:
15+
return {"maxTokens": 100, "tokenRatio": 0.1}
16+
17+
return {"maxTokens": 6, "tokenRatio": 0.1}
18+
19+
720
class RetryPolicy:
821
def __init__(
922
self,
1023
max_attempts: int = 4,
1124
status_codes: Tuple["grpc.StatusCode"] = (grpc.StatusCode.UNAVAILABLE,),
25+
throttling_mode: ThrottlingMode = ThrottlingMode.TEMPORARY,
1226
):
1327
self.__policy = {
1428
"methodConfig": [
@@ -21,10 +35,10 @@ def __init__(
2135
"backoffMultiplier": 2,
2236
"retryableStatusCodes": [status.name for status in status_codes],
2337
},
38+
"waitForReady": True,
2439
}
2540
],
26-
"retryThrottling": {"maxTokens": 100, "tokenRatio": 0.1},
27-
"waitForReady": True,
41+
"retryThrottling": get_throttling_policy(throttling_mode),
2842
}
2943

3044
def to_json(self) -> str:

0 commit comments

Comments
 (0)