Python(boto3)中的 DynamoDB TransactWriteItems
交易是 boto3 兩套 API 分歧最劇烈的地方之一:transact_write_items 只存在於低階 client 上,所以 Table 給你的那種原生 Python 型別便利,在這裡是沒有的。而當交易失敗時,你需要的東西藏在例外的一個角落,大多數 boto3 程式碼從來不會去看。(交易替你買到了什麼在每一套 SDK 裡都一樣。)
程式碼
import boto3
client = boto3.client("dynamodb")
# Move one award between two songs — atomically. If the first song has no
# award to give, NEITHER update happens.
try:
client.transact_write_items(
TransactItems=[
{
"Update": {
"TableName": "Music",
"Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
"UpdateExpression": "SET #upd0 = #upd0 - :one",
"ConditionExpression": "#upd0 >= :one",
"ExpressionAttributeNames": {"#upd0": "Awards"},
"ExpressionAttributeValues": {":one": {"N": "1"}},
}
},
{
"Update": {
"TableName": "Music",
"Key": {"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "A Mis Abuelos"}},
"UpdateExpression": "SET #upd0 = if_not_exists(#upd0, :zero) + :one",
"ExpressionAttributeNames": {"#upd0": "Awards"},
"ExpressionAttributeValues": {":one": {"N": "1"}, ":zero": {"N": "0"}},
}
},
]
)
print("Transaction committed")
except client.exceptions.TransactionCanceledException as e:
# One reason per action, in TransactItems order. Code "None" means that
# action was fine — some OTHER action sank the transaction.
codes = [reason["Code"] for reason in e.response["CancellationReasons"]]
print(f"Transaction canceled: {codes}") # e.g. ['ConditionalCheckFailed', 'None']說明
TransactItems— 一份由Put、Update、Delete與ConditionCheck字典組成的清單,每一個值都是 DynamoDB JSON,沒有例外。這是唯一一個型別化形式並非可選的 boto3 呼叫,也是本頁最後那一節存在的原因。上限請見 CLI 那一頁。CancellationReasons不在Error裡面。botocore 會把模型化的錯誤欄位提到回應字典的最上層,所以被攔下的例外帶著的e.response裡,CancellationReasons、Error、Message與ResponseMetadata是並排的。去e.response["Error"]底下找是找不到的,而e.response["Error"]只放摘要用的代碼與訊息。None那幾筆沒有"Message"— 成功動作的原因是只有一個鍵的字典{"Code": "None"},所以順手寫的[r["Message"] for r in reasons]剛好會在那些成功的動作上丟出KeyError: 'Message'。請用r.get("Message")。- 一個動態產生的例外類別 — botocore 會在執行期依服務模型建出
client.exceptions.TransactionCanceledException,這就是為什麼它掛在 client 實例上,也是為什麼你沒辦法from botocore.exceptions import ...匯入它。在一個作用域裡沒有 client 的輔助函式中,請攔botocore.exceptions.ClientError並依e.response["Error"]["Code"]分支;那個產生出來的類別是它的子類別。 - 結構性的錯誤不是以取消的形式抵達的,所以程式碼裡的
except從來不會看到它們。兩個動作瞄準同一個項目時,丟出的是一個代碼為ValidationException的裸ClientError,而它的e.response裡沒有CancellationReasons這個鍵,因為這筆交易在任何動作執行之前就被回絕了。如果你想用同樣的上下文把那些也記錄下來,請在外層攔ClientError。 - 在某個動作上加
ReturnValuesOnConditionCheckFailure: "ALL_OLD",會把落敗的項目以 DynamoDB JSON 放進那個動作的原因裡的Item鍵下,省下你在已經輸掉競賽之後還要再補一次get_item。 - boto3 會替你填
ClientRequestToken。在線路上擷取到的結果是,兩次一模一樣的transact_write_items呼叫帶著兩個不同的 UUID 離開,所以這個 token 覆蓋的是單一次呼叫,而不是你自己的攔截重試迴圈。如果重試可能活得比行程還久,請自己傳一個穩定的 token。 TransactionConflict要重試,ConditionalCheckFailed絕不重試 — 前者是說有別人短暫地佔住了那個項目;後者是說你的前提條件是假的,而且下一次還會是假的。這是大多數處理器唯一需要分開的兩個代碼,完整的一組則在 TransactionCanceledException 頁面上解碼。- 成本 — 交易式寫入的計費大約是同一筆寫入在交易外的兩倍,在 CLI 那一頁量過。如果你只需要在單一項目上取得不可分割性,條件式寫入以一半的價格就能買到。
這件事沒有 resource API 的版本
boto3.resource("dynamodb").Table(...) 沒有 transact_write_items 這個屬性;只有 resource.meta.client 才有。所以一份已經定案用 Table 加原生 Python 型別的程式碼庫,得為了交易退回型別化的 DynamoDB JSON,或用 boto3.dynamodb.types.TypeSerializer 手動序列化:
from boto3.dynamodb.types import TypeSerializer
serialize = TypeSerializer().serialize
values = {k: serialize(v) for k, v in {":one": 1, ":zero": 0}.items()}TypeSerializer 套用的規則跟 resource API 一樣,也就是說它會拒絕 float,並且對任何帶小數的東西都要求 decimal.Decimal。當你只是想把一個字面值貼進腳本時,DynamoDB JSON 轉換器會在瀏覽器裡做同樣的轉換。想在不手寫這兩種形式的情況下編輯交易碰到的那些項目,請下載 DynoTable。
相關範例
- Node.js 中的 DynamoDB TransactWriteItems — 用 AWS SDK v3 做同一筆交易。
- 用 AWS CLI 執行 DynamoDB TransactWriteItems — 從 shell 做同一筆交易。
- Python 中的 DynamoDB 條件式寫入 — 不用付 2 倍成本的單一項目不可分割性。
- DynamoDB 交易 — 隔離性、冪等性,以及交易值得動用的時機。
- DynamoDB TransactionCanceledException — 每一個取消原因代碼的解讀。
- 「Too many actions in a TransactWriteItems call」 — 交易的 100 個動作與 4 MB 限制。
- 「Transaction request cannot include multiple operations on one item」 — 每筆交易、每個項目只能有一個動作。
參考資料
- TransactWriteItems — Amazon DynamoDB API Reference
- DynamoDB.Client.transact_write_items — Boto3 documentation
- Amazon DynamoDB transactions: how it works — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
最後驗證於 2026-07-28,對照上方連結的 AWS 官方文件。