简体   繁体   English

如何使用 Boto3 和 Python 为 DynamoDB 设置重试延迟选项?

[英]How to set retry delay options for DynamoDB using Boto3 with Python?

I'm trying to avoid the ProvisionedThroughputExceededException by setting a custom exponential backoff using the "base" option like we can do in JavaScript according to this answer :我试图通过使用“base”选项设置自定义指数退避来避免ProvisionedThroughputExceededException ,就像我们可以根据这个答案在 JavaScript 中做的那样:

AWS.config.update({
  maxRetries: 15,
  retryDelayOptions: {base: 500}
});

As it's explained in this documentation , the "base" parameter defines the number used to increase the delay, so "base: 500" would increase the delay like this: 500, 1000, 1500, ...正如本文档中所解释的,“base”参数定义了用于增加延迟的数字,因此“base: 500”会像这样增加延迟:500、1000、1500...

I'm trying to make the same settings with Boto3 using Python 3.8, but the Boto3 Documentation seems to only allow setting the maximum retries attempts, not the delay options:我正在尝试使用 Python 3.8 对 Boto3 进行相同的设置,但Boto3 文档似乎只允许设置最大重试次数,而不是延迟选项:

from botocore.client import Config

config = Config(
   retries = {
      'max_attempts': 10,
      'mode': 'standard'
   }
)

The "mode" option only gets three values: "legacy", "standard" and "adaptive". “mode”选项仅获得三个值:“legacy”、“standard”和“adaptive”。 The doc also mentions a _retry.json file where these options are described and it looks like the "base" option is hardcoded in this file:该文档还提到了一个_retry.json文件,其中描述了这些选项,并且看起来“base”选项在此文件中被硬编码:

"dynamodb": {
      "__default__": {
        "max_attempts": 10,
        "delay": {
          "type": "exponential",
          "base": 0.05,
          "growth_factor": 2
        }

So, my question is: Is there any way to set the exponential backoff using Boto3 with Python for DynamoDB?所以,我的问题是:有没有办法使用 Boto3 和 Python 为 DynamoDB 设置指数退避?

This is hardcoded for Boto3 unfortunately & there is no way to modify the base retry delay using the Python SDK.不幸的是,这是为 Boto3 硬编码的,并且无法使用 Python SDK 修改基本重试延迟。 Unless you write your own wrapper around the SDK calls, this isn't possible out of the box.除非您围绕 SDK 调用编写自己的包装器,否则这是不可能的。

It may be worth to create an issue in the public repository for it to be picked up, or contribute directly.可能值得在公共存储库中创建一个问题以供拾取或直接贡献。

As mentioned by @Ermiya , I had to implement it myself.正如@Ermiya所提到的,我必须自己实现它。 I didn't want to modify the boto3 default settings, so we have to catch the exception and keep paginating from where it stops.我不想修改 boto3 默认设置,所以我们必须捕获异常并从它停止的地方继续分页。

This is how we can do:我们可以这样做:

Without Paginator (much simpler)没有分页器(简单得多)

from time import sleep
import boto3
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb', region_name='us-west-2')
db_table = dynamodb.Table('<my_table_name>')

query_params = {
        "TableName": '<my_dynamodb_table>',
        "IndexName": 'movies-index',
        "KeyConditionExpression": Key('movies').eq('thriller'),
}
retries = 1
max_retries = 6
while True:
    if retries >= max_retries:
        raise Exception(f'ProvisionedThroughputExceededException: max_retries was reached: {max_retries}')
    try:
        page = db_table.query(**selected_table_type)
    except ClientError as err:
        if 'ProvisionedThroughputExceededException' not in err.response['Error']['Code']:
            raise
        sleep(2 ** retries)
        retries += 1
        continue
    else:
        retries = 1
    yield from page.get('Items')
    if page.get('LastEvaluatedKey'):
        selected_table_type.update(
            ExclusiveStartKey=page['LastEvaluatedKey']
        )
        sleep(2)
    else:
        break

Using Paginator:使用分页器:

from time import sleep
import boto3
from boto3.dynamodb.conditions import Key
from boto3.dynamodb.conditions import ConditionExpressionBuilder
from boto3.dynamodb.types import TypeSerializer, TypeDeserializer

db_client = boto3.client('dynamodb', region_name='us-west-2')

td = TypeDeserializer()
ts = TypeSerializer()
query_params = {
        "TableName": '<my_dynamodb_table>',
        "IndexName": 'movies-index',
        "KeyConditionExpression": Key('movies').eq('thriller'),
    }
builder = ConditionExpressionBuilder()
condition = query_params["KeyConditionExpression"]
expr = builder.build_expression(condition, is_key_condition=True)
query_params.update({
    "KeyConditionExpression": expr.condition_expression,
    "ExpressionAttributeNames": expr.attribute_name_placeholders,
    "ExpressionAttributeValues": {k: ts.serialize(v) for k, v in expr.attribute_value_placeholders.items()},
})
total = 0
paginator = db_client.get_paginator('query')
pages = paginator.paginate(**query_params)
retries = 1
max_retries = 6
while True:
    if retries >= max_retries:
        raise Exception(f'ProvisionedThroughputExceededException: max_retries was reached: {max_retries}')
    try:
        for page in pages:
            retries = 1
            next_token = page.get('NextToken')
            for db_item in page.get('Items'):
                db_item = {k: td.deserialize(v) for k, v in db_item.items()}
                yield db_item
            total += page.get('Count')
            print(f"{total=}", end='\r')
    except ClientError as err:
        if 'ProvisionedThroughputExceededException' not in err.response['Error']['Code']:
            raise
        query_params.update(StartingToken=next_token)
        sleep(2 ** retries)
        retries += 1
    else:
        break
print(f"{total=}")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM