Some invariants span items, such as transferring a balance or enforcing uniqueness, and need atomic multi-item writes.
TransactWriteItems executes up to 100 actions (Put, Update, Delete, ConditionCheck) on distinct items, in one or more tables of the same account and region, atomically: either all succeed or none are applied. Each action can have its own condition; if any condition fails, the whole transaction is cancelled with TransactionCanceledException, whose CancellationReasons tell you which action failed. ConditionCheck lets you assert something about an item you do not modify.
TransactGetItems reads up to 100 items as a consistent snapshot.
Things to know:
- the total size limit is 4 MB, and one item cannot appear twice in a transaction
- transactional operations cost twice the normal capacity
- transactions conflict with concurrent writes to the same items and may be cancelled; retry them
- pass a
ClientRequestTokento make retries idempotent for 10 minutes
A classic use is enforcing uniqueness of a non-key attribute: write the user item and a second item with PK = EMAIL#ann@x.com guarded by attribute_not_exists, in one transaction.
Transactions are powerful but expensive; many designs need only single-item conditional writes.
Example
import boto3
client = boto3.client("dynamodb")
def register(user_id, email):
client.transact_write_items(TransactItems=[
{"Put": {"TableName": "App",
"Item": {"PK": {"S": f"USER#{user_id}"}, "SK": {"S": "PROFILE"},
"email": {"S": email}},
"ConditionExpression": "attribute_not_exists(PK)"}},
{"Put": {"TableName": "App", # uniqueness marker for the email
"Item": {"PK": {"S": f"EMAIL#{email}"}, "SK": {"S": "EMAIL"}},
"ConditionExpression": "attribute_not_exists(PK)"}},
])
register("u1", "ann@example.com")
try:
register("u2", "ann@example.com") # email taken: nothing is written
except client.exceptions.TransactionCanceledException as e:
print([r["Code"] for r in e.response["CancellationReasons"]])
# ['None', 'ConditionalCheckFailed']Remember
- Up to 100 actions, all-or-nothing, across tables in one region
- Any failed condition cancels the whole transaction
- Transactions cost double and can conflict; retry with a ClientRequestToken
- Marker items plus transactions enforce uniqueness of non-key attributes
Watch out for
- Using transactions for every write by default, doubling cost where a single conditional write was enough.