Skip to content

Commit 78cdf5c

Browse files
authored
Support retry in async client (#1613)
* Support retry in async client This adds support for retry in the async client. * Check allowed methods, remove 413
1 parent afbd1f4 commit 78cdf5c

File tree

2 files changed

+72
-10
lines changed
  • .generator/src/generator/templates
  • src/datadog_api_client

2 files changed

+72
-10
lines changed

.generator/src/generator/templates/rest.j2

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ from {{ package }}.exceptions import (
2121
logger = logging.getLogger(__name__)
2222

2323

24+
RETRY_AFTER_STATUS_CODES = frozenset([429, 500, 501, 502, 503, 504, 505, 506, 507, 509, 510, 511])
25+
RETRY_ALLOWED_METHODS = frozenset(["GET", "PUT", "DELETE", "POST", "PATCH"])
26+
27+
2428
class ClientRetry(urllib3.util.Retry):
25-
RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 500, 501, 502, 503, 504, 505, 506, 507, 509, 510, 511])
26-
DEFAULT_ALLOWED_METHODS = frozenset(["GET", "PUT", "DELETE", "POST", "PATCH"])
29+
RETRY_AFTER_STATUS_CODES = RETRY_AFTER_STATUS_CODES
30+
DEFAULT_ALLOWED_METHODS = RETRY_ALLOWED_METHODS
2731

2832
def get_retry_after(self, response):
2933
"""
@@ -235,6 +239,18 @@ class AsyncRESTClientObject:
235239
if configuration.proxy:
236240
proxy = aiosonic.Proxy(configuration.proxy, configuration.proxy_headers)
237241
self._client = aiosonic.HTTPClient(proxy=proxy)
242+
self._configuration = configuration
243+
244+
def _retry(self, method, response, counter):
245+
if (not self._configuration.enable_retry
246+
or counter >= self._configuration.max_retries
247+
or method not in RETRY_ALLOWED_METHODS
248+
or response.status_code not in RETRY_AFTER_STATUS_CODES):
249+
return 0
250+
retry_after = response.headers.get("X-Ratelimit-Reset")
251+
if retry_after is None:
252+
return self._configuration.retry_backoff_factor * (2 ** (counter))
253+
return int(retry_after)
238254

239255
async def request(
240256
self,
@@ -285,9 +301,23 @@ class AsyncRESTClientObject:
285301
request_body = compress.compress(request_body.encode("utf-8")) + compress.flush()
286302
elif headers.get("Content-Encoding") == "deflate":
287303
request_body = zlib.compress(request_body.encode("utf-8"))
288-
response = await self._client.request(
289-
url, method, headers, query_params, request_body, timeouts=request_timeout
290-
)
304+
elif headers.get("Content-Encoding") == "zstd1":
305+
import zstandard as zstd
306+
307+
compressor = zstd.ZstdCompressor()
308+
request_body = compressor.compress(request_body.encode("utf-8"))
309+
counter = 0
310+
while True:
311+
response = await self._client.request(
312+
url, method, headers, query_params, request_body, timeouts=request_timeout
313+
)
314+
retry = self._retry(method, response, counter)
315+
if not retry:
316+
break
317+
import asyncio
318+
319+
await asyncio.sleep(retry)
320+
counter += 1
291321

292322
if not 200 <= response.status_code <= 299:
293323
data = b""

src/datadog_api_client/rest.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
logger = logging.getLogger(__name__)
2424

2525

26+
RETRY_AFTER_STATUS_CODES = frozenset([429, 500, 501, 502, 503, 504, 505, 506, 507, 509, 510, 511])
27+
RETRY_ALLOWED_METHODS = frozenset(["GET", "PUT", "DELETE", "POST", "PATCH"])
28+
29+
2630
class ClientRetry(urllib3.util.Retry):
27-
RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 500, 501, 502, 503, 504, 505, 506, 507, 509, 510, 511])
28-
DEFAULT_ALLOWED_METHODS = frozenset(["GET", "PUT", "DELETE", "POST", "PATCH"])
31+
RETRY_AFTER_STATUS_CODES = RETRY_AFTER_STATUS_CODES
32+
DEFAULT_ALLOWED_METHODS = RETRY_ALLOWED_METHODS
2933

3034
def get_retry_after(self, response):
3135
"""
@@ -237,6 +241,20 @@ def __init__(self, configuration):
237241
if configuration.proxy:
238242
proxy = aiosonic.Proxy(configuration.proxy, configuration.proxy_headers)
239243
self._client = aiosonic.HTTPClient(proxy=proxy)
244+
self._configuration = configuration
245+
246+
def _retry(self, method, response, counter):
247+
if (
248+
not self._configuration.enable_retry
249+
or counter >= self._configuration.max_retries
250+
or method not in RETRY_ALLOWED_METHODS
251+
or response.status_code not in RETRY_AFTER_STATUS_CODES
252+
):
253+
return 0
254+
retry_after = response.headers.get("X-Ratelimit-Reset")
255+
if retry_after is None:
256+
return self._configuration.retry_backoff_factor * (2 ** (counter))
257+
return int(retry_after)
240258

241259
async def request(
242260
self,
@@ -287,9 +305,23 @@ async def request(
287305
request_body = compress.compress(request_body.encode("utf-8")) + compress.flush()
288306
elif headers.get("Content-Encoding") == "deflate":
289307
request_body = zlib.compress(request_body.encode("utf-8"))
290-
response = await self._client.request(
291-
url, method, headers, query_params, request_body, timeouts=request_timeout
292-
)
308+
elif headers.get("Content-Encoding") == "zstd1":
309+
import zstandard as zstd
310+
311+
compressor = zstd.ZstdCompressor()
312+
request_body = compressor.compress(request_body.encode("utf-8"))
313+
counter = 0
314+
while True:
315+
response = await self._client.request(
316+
url, method, headers, query_params, request_body, timeouts=request_timeout
317+
)
318+
retry = self._retry(method, response, counter)
319+
if not retry:
320+
break
321+
import asyncio
322+
323+
await asyncio.sleep(retry)
324+
counter += 1
293325

294326
if not 200 <= response.status_code <= 299:
295327
data = b""

0 commit comments

Comments
 (0)