Transactions

Amazon DynamoDB · Advanced · 14 min

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 ClientRequestToken to 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.

Check yourself

  1. A TransactWriteItems with three Puts fails the condition on the second Put. What is written?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html

Builds on

Unlocks

Nothing builds on this yet.