transactWrite в DynamoDB не является ACID в нескольких таблицах
У меня есть две таблицы в 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"
}
}
]
}
Итак, вот два
Put
операции происходят на
dev-table
и один
Update
операция идет
dev-table-metadata
Я запускаю
transactWrite
в цикле 10 раз, и ответ возвращается нормально, но за несколько итераций я получаю ошибку ниже
TransactionCanceledException: Transaction cancelled, please refer cancellation reasons for specific reasons [None, None, TransactionConflict]
Я предполагаю, что в этих случаях вся транзакция должна быть отменена, но первые две операции Put записываются в таблицу, а последнее обновление - нет.
1 ответ
Я попытался воспроизвести это с помощью сценария, который вы можете увидеть ниже, с двумя операциями помещения в одну таблицу и обновлением в другую в транзакции. Я не смог воспроизвести это. Пожалуйста, включите код, чтобы сделать это воспроизводимым.
Помимо этого, я склонен доверять документации AWS и своему опыту, что это действительно ACID.
- это синхронная и идемпотентная операция записи, которая объединяет до 25 действий записи в одну операцию "все или ничего". Эти действия могут быть нацелены на до 25 отдельных элементов в одной или нескольких таблицах DynamoDB в одной учетной записи AWS и в одном регионе. Совокупный размер элементов транзакции не может превышать 4 МБ. Действия завершаются атомарно, так что либо все они успешны, либо ни одно из них не удается. - документы (выделено мной)
Моя попытка воспроизвести это:
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)