【问题标题】:transactWrite on DynamoDB is not ACID across multiple tablesDynamoDB 上的 transactWrite 不是跨多个表的 ACID
【发布时间】:2021-04-21 22:58:18
【问题描述】:

我在 DynamoDB 中有两个表,想对这两个表执行 PUT 和 Update 操作。以下是参数:

{
    "TransactItems": [
        {
            "Put": {
                "Item": {
                    "ref": 0,
                    "date": 0
                },
                "TableName": "dev-table"
            }
        },
        {
            "Put": {
                "Item": {
                    "ref": 1,
                    "date": 0
                },
                "TableName": "dev-table"
            }
        },
        {
            "Update": {
                "TableName": "dev-table-metadata",
                "Key": {
                    "metaDataKey": "refConsumed"
                },
                "ExpressionAttributeValues": {
                    ":s": 1
                },
                "UpdateExpression": "SET metaDataValue = :s",
                "ReturnValues": "ALL_NEW"
            }
        }
    ]
}

因此,dev-table 上发生了两个 Put 操作,dev-table-metadata 上发生了一个 Update 操作

我在循环中触发transactWrite 10 次,返回的响应正常,但是对于几次迭代,我得到以下错误

TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [None, None, TransactionConflict]

我的假设是,在这些情况下,整个事务应该被取消,但前两个 Put 操作被写入表,而最后一次更新不是。

【问题讨论】:

  • 您是否总是发送相同的交易?在这种情况下,它仍然可能是以前请求的结果。
  • 每次迭代的所有三个操作都有新的价值。我在节点 lambda 中执行此操作,如下所示 let dynamo = new AWS.DynamoDB.DocumentClient(); dynamo.transactWrite(params, function(err, data) { ... })
  • 我设法找出了这种行为背后的原因。这是因为TransactWriteItems 命令的异步性质。所以基本上在从 1 到 10 的 for 循环中,触发了 10 个TransactWriteItems 命令并且它们以异步方式完成。所以当我查看最后的 db 表时,我认为这些事务不是 ACID。根据我的用例,我需要以同步方式运行这些。无论如何感谢您的帮助@Maurice

标签: amazon-web-services amazon-dynamodb


【解决方案1】:

我尝试使用您可以在下面看到的脚本来重现这一点,其中对一个表进行了两次放置操作,并在事务中对另一个表进行了更新。 我无法重现此内容。 请包含代码以使其可重现。

除此之外,我倾向于相信 AWS 文档和我的经验,即这实际上是符合 ACID 的。

TransactWriteItems 是一种同步且幂等的写入操作,可将多达 25 个写入操作组合到一个全有或全无操作中。这些操作最多可以针对同一 AWS 账户和同一区域中的一个或多个 DynamoDB 表中的 25 个不同项目。事务中项目的总大小不能超过 4 MB。 动作以原子方式完成,要么全部成功,要么都不成功。

docs(强调我的)

我试图重现这个:

import typing

import boto3
import boto3.dynamodb.conditions as conditions

from botocore.exceptions import ClientError

TABLE_1 = "tbl1"
TABLE_2 = "tbl2"


def create_table_if_not_exists(table_name: str):

    try:
        boto3.client("dynamodb").create_table(
            AttributeDefinitions=[{"AttributeName": "PK", "AttributeType": "S"}],
            TableName=table_name,
            KeySchema=[{"AttributeName": "PK", "KeyType": "HASH"}],
            BillingMode="PAY_PER_REQUEST"
        )
    except ClientError as err:
        if err.response["Error"]["Code"] == 'ResourceInUseException':
            # Table already exists
            pass
        else:
            raise err

def transact_write(put_1_value, put_2_value, update_1_value):
    client = boto3.client("dynamodb")
    client.transact_write_items(
        TransactItems=[
            {
                "Put": {
                    "Item": {"PK": {"S": put_1_value}},
                    "TableName": TABLE_1
                }
            },
            {
                "Put": {
                    "Item": {"PK": {"S": put_2_value}},
                    "TableName": TABLE_1
                }
            },
            {
                "Update": {
                    "TableName": TABLE_2,
                    "Key": {
                        "PK": {"S": str(update_1_value)}
                    },
                    "UpdateExpression": "SET itemValue = :value",
                    "ExpressionAttributeValues": {
                        ":value": {"S": "some_value"}
                    }
                }
            }

        ]
    )

if __name__ == "__main__":
    create_table_if_not_exists(TABLE_1)
    create_table_if_not_exists(TABLE_2)

    for n in range(100):
        print(f"Write #{n}")
        transact_write("a", "b", 1)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-18
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    • 2018-08-20
    • 2016-08-05
    • 1970-01-01
    相关资源
    最近更新 更多