DynamoDB TransactWriteItems di Python (boto3)

Transaksi adalah salah satu tempat di mana dua API boto3 paling berjauhan: transact_write_items hanya ada pada client tingkat rendah, jadi kenyamanan tipe-Python-native yang Anda dapat dari Table tidak tersedia di sini. Dan ketika transaksinya gagal, yang Anda butuhkan berada di sudut exception yang jarang dilihat kode boto3 mana pun. (Apa yang Anda dapat dari sebuah transaksi sama di setiap SDK.)

Kode

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']

Penjelasan

  • TransactItems — sebuah list berisi dict Put, Update, Delete dan ConditionCheck, setiap nilainya dalam DynamoDB JSON, tanpa pengecualian. Ini satu-satunya panggilan boto3 di mana bentuk bertipe itu bukan pilihan, dan itulah alasan bagian di akhir halaman ini ada. Batasannya ada di halaman CLI.
  • CancellationReasons tidak berada di dalam Error. botocore mengangkat field error yang dimodelkan ke tingkat teratas dict respons, jadi exception yang tertangkap membawa e.response dengan key CancellationReasons, Error, Message dan ResponseMetadata berdampingan. Mencarinya di bawah e.response["Error"] tidak menemukan apa pun, dan e.response["Error"] hanya berisi kode dan pesan ringkasnya.
  • Tidak ada "Message" pada entri None — alasan untuk aksi yang berhasil adalah dict berkunci tunggal {"Code": "None"}, jadi [r["Message"] for r in reasons] yang tampak alami itu melempar KeyError: 'Message' tepat pada aksi-aksi yang berhasil. Pakai r.get("Message").
  • Kelas exception yang dibangkitkan — botocore membangun client.exceptions.TransactionCanceledException dari model layanan saat runtime, itulah sebabnya ia menempel pada instance client dan sebabnya Anda tidak bisa from botocore.exceptions import ... untuknya. Di dalam helper yang tidak punya client dalam scope, tangkap botocore.exceptions.ClientError dan bercabanglah pada e.response["Error"]["Code"]; kelas yang dibangkitkan itu subclass-nya.
  • Kesalahan struktural tidak datang sebagai pembatalan, jadi klausa except dalam cuplikan itu tak pernah melihatnya. Dua aksi yang menyasar item yang sama melempar ClientError polos dengan kode ValidationException dan e.response-nya tidak punya key CancellationReasons, karena transaksinya ditolak sebelum aksi mana pun berjalan. Tangkap ClientError di lapisan terluar kalau Anda ingin itu tercatat dengan konteks yang sama.
  • ReturnValuesOnConditionCheckFailure: "ALL_OLD" pada sebuah aksi menaruh item yang kalah di bawah key Item dalam alasan aksi tersebut, dalam DynamoDB JSON, sehingga Anda tak perlu get_item susulan setelah kalah balapan.
  • boto3 mengisi ClientRequestToken untuk Anda. Ditangkap di wire, dua panggilan transact_write_items yang identik berangkat dengan dua UUID berbeda, jadi token itu mencakup satu panggilan saja dan bukan loop catch-and-retry Anda sendiri. Berikan token yang stabil sendiri kalau percobaan ulangnya bisa hidup lebih lama dari proses Anda.
  • Coba ulang pada TransactionConflict, jangan pernah pada ConditionalCheckFailed — yang pertama berarti orang lain memegang item itu sesaat; yang kedua berarti prasyarat Anda salah dan akan tetap salah lain kali. Hanya dua kode itu yang perlu dipisahkan sebagian besar handler, dan set lengkapnya diuraikan di halaman TransactionCanceledException.
  • Biaya — penulisan transaksional menagih kira-kira dua kali lipat penulisan yang sama di luar transaksi, diukur di halaman CLI. Kalau Anda hanya butuh atomisitas pada satu item, penulisan bersyarat memberikannya dengan setengah harga.

Tidak ada versi API resource untuk yang satu ini

boto3.resource("dynamodb").Table(...) tidak punya atribut transact_write_items; hanya resource.meta.client yang punya. Jadi codebase yang sudah mapan dengan Table dan tipe Python native harus mundur ke DynamoDB JSON bertipe untuk transaksinya, atau melakukan serialisasi manual dengan 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 menerapkan aturan yang sama dengan API resource, artinya ia menolak float dan mengharapkan decimal.Decimal untuk apa pun yang pecahan. Konverter DynamoDB JSON melakukan konversi yang sama di browser ketika Anda cuma perlu menempelkan sebuah literal ke dalam skrip. Untuk menyunting item yang disentuh sebuah transaksi tanpa menulis kedua bentuk itu secara manual, unduh DynoTable.

Contoh terkait

Referensi

Terakhir diverifikasi 2026-07-28 terhadap dokumentasi resmi AWS yang ditautkan di atas.

Bekerja dengan DynamoDB tanpa Console

Klien desktop DynamoDB yang cepat dan menjalankan SQL sungguhan yang tidak bisa dijalankan DynamoDB — JOINs, GROUP BY, agregasi — dengan editing visual dan agen AI pada kunci Bedrock milik Anda sendiri.

Uji coba gratis 30 hari, tanpa kartu kredit — lalu paket Free tanpa batas waktu.